TypeScript 中可能你不知道的关键字 infer

121 阅读2分钟

在 TypeScript 中,infer 关键字通常与条件类型一起使用,在条件类型中定义泛型里面推断出来的类型参数,从而可以在后续的类型计算中使用这个类型。

1、用法

(1) 条件类型语法

T extends U ? X : Y

上面使用 extends 关键字判断类型 T 是否可以被安全地视为类型 U 的一个子类型,如果是那么条件类型的结果就是 X,否则结果就是 Y

(2) 条件类型使用 infer

例子1:

type F<T> = T extends Array<infer K> ? K : T;

上面例子中,T extends Array<infer K> 表示如果参数 T 是一个数组,那么就将该数组的成员类型推断出来并定义为 K,并且条件类型的结果为 K;如果参数 T 不是数组类型,那么条件类型的结果为 T

下面使用 F<T> 类型:

// string
type S = F<string[]>;

// boolean
type B = F<boolean>;

上面代码中:

F<string[]> 传入的类型参数(T)是 string[],TypeScript 推断出数组成员类型是 string,即 infer K 中的 K 代表的是类型 string,所以最终返回的是类型 string

F<boolean> 传入的类型参数是 boolean,它不是数组,所以直接返回自身。

如果不用 infer 定义类型参数,那么就要传入两个类型参数,很是麻烦。

type F<T, K> = T extends Array<K> ? K : T;

例子2:

type PromiseFun<T> = T extends (...args: infer P) => infer K
  ? (...args: P) => Promise<K>
  : T;

上面例子中,如果 T 是函数,就返回这个函数的 Promise 版本,否则原样返回。infer P 表示该函数的参数类型为 Pinfer K 表示该函数的返回值类型为 K

例子3:

type NiceType<T> = T extends {
  x: infer U;
  y: infer V;
}
  ? [U, V]
  : never;

// 用法示例
type NT1 = NiceType<{ a: string; b: number }>;
// never

type NT2 = NiceType<{ x: string; y: number }>;
// [string, number]

上面示例中,infer 提取了参数对象的属性 x 和属性 y 的类型。

例子4:

type Str = "s-b";
type Bar = Str extends `s-${infer rest}` ? rest : never; 

// 相当于
type Bar = "b"

上面例子中,rest 是从模板字符串提取的类型参数,这里是 'b'