如何使用 never 类型
- 由于永远不能给 never 类型赋值,所以可以用它来对各种函数用例施加限制;
- 目的就是写出类型绝对安全的代码 (代码错误,提示改错);
确保对 switch 和 if-else 语句中的所有条件都做处理
如果一个函数只能接受一个 never 的参数,那么这个函数就永远不能用任何非 never 的值来调用 (不传 never 类型 TypeScript 编译器会发出警报);
可以用这类函数来确保 switch 和 if-else 语句中,每个条件都覆盖了处理方法:将其放在 default 条件中,可以确保每个条件都被处理,否则取值必须是 never 类型,如果不小心漏掉了一个可能的条件,会得到一个类型错误,如下:
function unknownColor(x: never): never {
throw new Error("unknown color");
}
// Color 新增了一个 blue' 类型,getColorName 中没有进行处理
type Color = 'red' | 'green' | 'blue'
function getColorName(c: Color): string {
switch (c) {
case 'red':
return 'is red';
case 'green':
return 'is green';
default:
// const unknownColor: never = c; // 等价
return unknownColor(c); // ❌ Argument of type 'string' is not assignable to parameter of type 'never'
}
}
禁用结构化类型中的一部分
假设有一个函数,它接受一个 VariantA 类型或 VariantB 类型的参数,但是不能接受一个同时包含两种类型所有属性的类型,即两种类型的一个子类型;
使用 never 后,就可以将类型结构中的部分给禁用掉,从而阻止用户向其传递包含两种类型属性的对象;
type VariantA = {
a: string
b?: never // 使用 never 限制
}
type VariantB = {
b: number
a?: never // 使用 never 限制
}
declare function fn(arg: VariantA | VariantB): void
fn({ a: 'foo' }) // ✅
fn({ b: 123 }) // ✅
fn({ a: 'foo', b: 123 }) // ❌ Types of property 'a' are incompatible
用于表示理论上无法到达的条件分支
当在条件类型中使用 infer 创建一个类型变量时,必须为每个 infer 关键字创建 else 分支:
type A = 'foo';
type B = A extends infer C
? (C extends 'foo' ? true : false) // 在此表达式中,C 等同于 A
: never // 这个分支永远不会执行,但是也不能不写它
从映射类型中过滤属性
在 TypeScript 中,类型是不可变的,如果想要从一个对象类型中删除一个属性,可以通过新建一个类型、转换和过滤达到这个目的,而我们只要在映射类型中用条件做重映射就可以达到相同的效果;
以下 Filter 类型,是基于
对象类型的值
对对象类型
进行筛选的例子:
type Filter<Obj extends Object, ValueType> = {
[Key in keyof Obj as (ValueType extends Obj[Key] ? Key : never)]: Obj[Key]
}
interface Foo {
name: string;
id: number;
}
// 过滤 Foo,留下 string 类型的字段
type Filtered = Filter<Foo, string>; // { name: string }
// 过滤 Foo,留下 number 类型的字段
type Filtered2 = Filter<Foo, number>; // { id: number }
// 过滤 Foo,留下 boolean 类型的字段
type Filtered3 = Filter<Foo, boolean>; // { }
如何检查类型推导是否为 never
-
检查一个类型是否会推导为 never 比想象中要难得多,思考以下代码:
type IsNever<T> = T extends never ? true : false type Res = IsNever<never> // never 🧐,淦
-
原因可以总结为:
- TypeScript 会自动将联合类型分发为条件类型;
- never 是一个空联合类型;
- 因此,当分发发生时,没有内容可分发,所以条件类型再次将其推导为 never;
-
唯一的解决方法是不使用隐式分发,而是将类型参数封装在一个元组中:
type IsNever<T> = [T] extends [never] ? true : false; type Res1 = IsNever<never> // 'true' ✅ type Res2 = IsNever<number> // 'false' ✅
TypeScript👉 其他数据类型
上一篇