热搜:fiddler git ip ios 代理
历史搜索

TypeScript 的 never 类型

游客2024-11-05 12:03:01
目录文章目录
  1. never 的特点
  2. 函数中的 never
  3. never 和 void 的区别
  4. never 作为可变类型守卫
  5. 详尽检查
  6. 结论

对于很多人来说,TypeScript 中的 never 类型比较神秘,它有什么作用?什么时候应该使用它?今天我们就来深入讨论 never 类型,并介绍可能遇到的情况。

never 的特点

TypeScript 使用 never 关键字来表示逻辑上不应该发生的情况和控制流。实际上,我们在工作中不会常遇到使用 never 的情况,但是还是很有必要了解它是如何有助于 TypeScript 的类型安全的。

官方文档对 never 的描述:

never 类型是任何类型的子类型,也可以赋值给任何类型;但是,没有类型是 never 的子类型或可以赋值给 never 类型(never 本身除外)。

也就是说,可以将 never 类型的变量分配给任何其他变量,但不能将其他变量分配给never。下面来看一个例子:

const throwErrorFunc = () => { throw new Error("error") };
let neverVar: never = throwErrorFunc()
const myString = ""
const myInt:number = neverVar;
neverVar = myString // Type 'string' is not assignable to type 'never'

我们可以暂时忽略 throwErrorFunc 的功能,只需知道,这样可以初始化类型为 never 的变量。

从上面的代码中可以看到,可以将 never 类型的变量 neverVar 分配给 number 类型的变量 myInt。但是,不能将 string 类型的 myString 变量分配给 neverVar,这样分配会报错。这也就是上面所说的,不能将任何其他类型的变量分配给 never,即使是 any 类型的变量。

函数中的 never

TypeScript 使用 never 作为那些无法达到的终点的函数的返回值类型。主要有两种情况:

  • 函数抛出一个错误异常。
  • 函数包含一个无限循环。

来看上面提到的 throwErrorFunc 函数,TypeScript 就会推断此函数的返回类型为 never

const throwErrorFunc = () => {
  throw new Error("error")
};

另一种情况就是如果有一个一直为真的表达式产生了无限循环,它没有中断或者返回语句。TypeScript 就会推断此函数的返回类型为 never:

const output = () => {
  while (true) {
    console.log("循环");
  }
};

never 和 void 的区别

那什么是 void 类型呢?我们有 void 为什么还要 never 类型呢?

nevervoid 的主要区别在于,void 类型的值可以是 undefined 或 null。

TypeScript 对不返回任何内容的函数使用 void。如果没有为函数指定返回类型,并且在代码中没有返回任何内容,TypeScript 将推断其返回类型为void。在 TypeScript 中,不返回任何内容的 void 函数实际上返回的是 undefined。

来看一个例子:

const functionWithVoidReturnType = () => {}; 
console.log(functionWithVoidReturnType()); // undefined

这里,TypeScript 会推断此函数的返回类型为 void。我们通常会忽略 void 函数的返回值。

这里需要注意:根据 never 类型的特征,我们不能将 void 指定给 never

const myVoidFunction = () => {}
neverVar = myVoidFunction() // ERROR: Type 'never' is not assignable to type 'void'

never 作为可变类型守卫

如果变量被一个永远不可能为 true 的类型保护缩小范围,那么变量就可能成为 never 类型。通常,这表明条件逻辑存在缺陷。

来看下面的例子:

const unExpectedResult = (myParam: "this" | "that") => {
  if (myParam === "this") {
    
  } else if (myParam === "that") {
    
  } else {
    console.log({ myParam })
  }
}

在这个例子中,当函数执行到 console.log({ myParam }) 时,myParam 的类型将为 never。

这是因为我们将 myParam 的类型设置为了 this 或 that。由于 TypeScript 认为 myParam 类型属于两个类型之一,所以从逻辑上讲,第三个 else 语句永远不会出现。所以 TypeScript 就会将参数类型设置为 never

详尽检查

在实践中可能会用到 never 的一个地方就是进行详细的检查。这有助于确保我们处理了代码中的每个边缘情况。

下面来看看如何使用详尽检查为 switch 语句添加更好的类型安全性:

type Animal = "cat" | "dog" | "bird"

const shouldNotHappen = (animal: never) => {
  throw new Error("error")
}

const myPicker = (pet: Animal) => {
  switch(pet) {
    case "cat": {
      // ...
      return
    }
    case "dog": {
      // ...
      return
    }
  }
  return shouldNotHappen(pet)
}

当添加 「return shouldNotHappen(pet)」 时,会看到一个错误提示:

TypeScript 的 never 类型 1

这里的错误提示告诉我们,忘记包含在 switch 语句中的情况。这是一种获得编译时安全性并确保处理 switch 语句中的所有情况的巧妙模式。

结论

如你所见,TypeScript 中的 never 类型在特定情况很有用。大多数情况下,never 表明代码存在缺陷。

但在某些情况下,例如详尽检查,它可以成为帮助编写更安全的 TypeScript 代码的好工具。