今天来说一说TypeScript中,一个看起来比较奇怪的 never 类型。
官方文档定义:
never 类型表示的是那些永不存在的值的类型。 例如, never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never 类型,当它们被永不为真的类型保护所约束时。
官方文档中举了两个函数例子第一个函数是会抛出异常的函数,因此永远不会正常返回值,所以可以定义该函数的返回值为 never;第二个函数是一个 while(true){} 的函数,因此会陷入永久循环,也不会正常返回值,所以可以定义该函数的返回值为 never。这两个函数都是永远不会到达终点的,虽然 never 类型的特点能够很好地被这两个函数解释,但是当你细想后就能很快发现,在一般情况下谁会写出一个永远不会到达终点的函数呢,那么我们又可以利用 never 去做什么呢?
看下列代码,如果有一个联合类型 all,包含 Foo 或者 Bar,当我们在使用 switch 判断类型的时候,传入为 all 类型的参数 val 会相应收窄类型,在 default 中 val 就是 never 类型,当我们在 default 中将 val 赋给一个显示声明为 never 类型的变量,在 ts 的编译过程中是可以通过的:
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
type all = Foo | Bar
function handleValue(val: all) {
switch (val.type) {
case 'foo':
// 类型收窄:val 被收窄为 'Foo'
break
case 'bar':
// 类型收窄:val 被收窄为 'Bar'
break
default:
// val 为 never 类型
const neverValue: never = val
break
}
}
如果有一天我们修改了 all 类型,在 all 类型中又添加了一个 Baz 类型:
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
interface Baz {
type: 'baz'
}
type all = Foo | Bar | Baz
但是如果我们忘了在 switch 分支中添加 Baz 类型的判断,那么这个时候的 default 内 val 的类型就会收窄为 Baz 类型,而不是 never 类型,但是我们却将 val 赋给了一个 never 类型的变量,于是便出现了编译出错的情况。因此,我们可以利用 never 类型来阻止我们犯类似的错误。
简要说明一下类型收窄和类型拓宽的意思:
类型收窄就是从宽类型转换为窄类型的过程,一般用于声明为联合类型的变量,如上面所示,在 switch 中将一个具有两种类型的变量收窄为一个类型的变量,还有一个常见的例子就是非空检查,例如一个变量的类型是 string 或 null,当其不为空的时候正常处理,否则给出警告:
let el: string | null = 'string'
if (el) {
// 类型为 string
// 排除了为空的情况
} else {
// 类型为null
alert('el is null');
}
类型拓宽与类型收窄相反,具体指的是在一些情况下,ts 可以根据上下文自动推断变量的类型,减少了显示声明类型的需要,例如定义一个变量 let name = 'mary',ts 会自动推断 name 为 string 基本类型,我们不必再去显示声明它的类型,要了解更多,可点击:理解 TypeScript 类型拓宽