infer
关键字解析使用 条件类型 来应用约束然后 提取 出类型,是一种非常常见的操作。比如:
type Flatten<T> = T extends any[] ? T[number] : T;
这里我们定义了一个类型工具 Flatten<T>,你可以理解为一个接受类型作为参数并返回一个类型的函数,其中尖括号中的T
(<T>)就是那个类型函数的参数。
后面相当于类型函数的部分是一个 条件类型表达式,形式看起来有点像 JavaScript 中的条件表达式,事实上你也可以借助其进行理解。其语法格式为:
SomeType extends OtherType ? TrueType : FalseType;
与 JavaScript 条件表达式以 逻辑真假 为判断依据类似,
在 Typescript 条件类型表达式 中,以 左侧的类型可分配给右侧的类型 为依据。
在上面的代码 (T extends any[] ? T[number] : T
) 中,如果传如的类型 T
可以分配给类型 any[]
(比如T是一个string[]
) ,则返回类型 T[number]
,否则返回 T
本身。
比如,我们调用该类型工具生成一个新的类型 MyType
:
type MyType = Flatten1<['a',2]>; // 2 | "a"
由于类型 ['a',2]
可以分配给 any[]
(或者说是 any[]
的子类型,两者就像继承关系,所以用extends 关键字) 提取出了其中的类型作为一个新的类型 MyType
。
infer
关键字表达提取Typescript 为我们提供了一种 使用 infer
关键字 从我们在 条件类型的 true 分支中 比较的类型进行推断的方法。
换句话说, infer
关键字用于声明一个动态生成的 类型变量,这个类型变量是用于临时存放被捕获的类型。例如:
type Flatten<T> = T extends Array<infer I> ? I : T;
这个例子中,要求传入的类型 T
可分配给 Array<infer I>
,我们知道泛型 Array<U>
中的 U
表示约束该数组的成员类型都为 U
,但是 <infer I>
却不会再约束 数组的所有成员都为同一个类型 I
。这里的区别在于:
在下面这个表达式中:
T extends Array<U> ? I : T;
T
必须是所有成员类型一致的情况下才能分配给 Array<U>
。
比如数组 [1,2,3]
可分配给 number[]
,进一步还可分配给 Array<U>
这都是兼容的。但是 [1,'a','b']
由于其中成员类型不一样,它不能分配给 Array<U>
。
然而在下面这个表达式中:
T extends Array<infer I> ? I : T;
infer I
是 动态生成的一个个新类型 ,因此这看起来就像是在使用 any[]
,说明不论数组中的值是何种类型,只要是一个数组,就满足 T extends Array<infer I>
,并且满足的同时 infer I
表示被推断出来的 联合类型。
lib.es5.d.ts
的例子这部分类型工具声明摘抄于 typescript 内置的类型声明模块lib.es5.d.ts
。包括:
ThisParameterType
OmitThisParameter
Parameters
ReturnType
InstanceType
Awaited
他们的声明均使用了 infer
关键字
type ThisParameterType<T> =
T extends (this: infer U, ...args: never) =>
any ? U : unknown;
解释:
type OmitThisParameter<T> =
unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;
type InstanceType<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: any) => infer R ? R : any;
type Awaited<T> =
T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
F extends ((value: infer V, ...args: infer _) => any) ? // 如果`then`的参数是可调用的,则提取第一个参数
Awaited<V> : // recursively unwrap the value
never : // `then`这个参数是不可调用的
T; // non-object 或者 non-thenable
never
。这模仿了 await
的行为。