持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
1. 引言
什么是infer关键字?它有什么用呢?
首先infer关键字一般配合extends中的条件判断用于条件中的类型推导。可以是判断条件值推导,也可以是返回值推导。
2. 含义
在这里我们套用官网的一个例子:提取函数返回值
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type a = ReturnType2<()=> string> // type a = string
type b = ReturnType2<string> // type b = any
让我们试着解读下上述例子,ReturnType类型接受一个泛型形参,使用extends类型条件判断 判断传入形参T是否可赋值(...args: any[]) => infer R 这个箭头函数,如果可以说明传入的函数符合。这时候大家会问箭头函数的默认返回值infer R 是什么? 其实,这个也是像泛型变量一样的一个“占位符”,用于不同场景函数在条件类型判断成立的情况下可以返回不同场景的返回值,使用infer R告知程序它是函数运行的 ==“插槽”== ,根据函数规则默认是该返回值。
这时候也需会有同学问了,既然只是要获取返回值,假使我们使用一个函数,接受一个数组形参,最后返回数组的第一项的话,那么为什么不直接使用泛型呢?就像如下
function (...argument: T []): T[0]
解答:但我们传入的不是数组,是对象呢?0不算对象的key值,T[0]这样是获取不到对象值,会直接报错。
3. 作用
- 条件类型判断中可以简化代码量(请看例子一)
- 如何获取Promise中的类型参数(请看例子二)
- 可作为类型推导(请看例子三)
- 在TypeScript 4.7 版本中引入了一个infer的简洁写法。我们通过++例子四++来了解下
4. 例子
例子一:
type IdsArr = number[]
type NamesArr = string[]
type ExtractFun<T> = T extends IdsArr ? number : T extends NamesArr ? string : T
type IdsArrType = ExtractFun<IdsArr> // type IdsArrType = number
type NamesArrType = ExtractFun<NamesArr> // type NamesArrType = string
type AnyArrType = ExtractFun<true> // type AnyArrType = true
// 使用infer关键字推导后,简化代码
type ExtractInferFun<T> = T extends (infer R)[] ? R : T
type IdsArrType2 = ExtractInferFun<IdsArr> // type IdsArrType2 = number
type NamesArrType2 = ExtractInferFun<NamesArr> // type NamesArrType2 = string
type AnyArrType2 = ExtractInferFun<true> // type AnyArrType2 = true
type TuleArr = ExtractInferFun<[number,string]> // type TuleArr = number | string
例子二:
type ResponseTypeVal = Promise<string[]>
type PickPromise<T> = T extends Promise<infer R> ? R : T
type C = PickPromise<ResponseTypeVal> // type c = string[]
例子三:
type InferenceType<T> = T extends {a: infer U,b: infer U} ? U : any
type D = InferenceType<{a: string,b: string}> // type d = string
type E = InferenceType<{a: number,b: string}> // type d = number | string
例子四:
type NoConcise<T> = T extends [infer R ,...unknown[]] ? R extends string ? R : never : never
type F = NoConcise<['sam1',true,121]> // type F = 'sam1'
type G = NoConcise<[string,true,121]> // type G = string
type H = NoConcise<[number | string,true,121]> // type H = string
.
// 像这样甚至跟深层的条件判断,有没有更简单的写法呢,答案是有的
type Concise <T> = T extends [infer R extends string,...unknown[]] ? R : never
type F2 = NoConcise<['sam1',true,121]> // type F2 = 'sam1'
type G2 = NoConcise<[string,true,121]> // type G2 = string
type H2 = NoConcise<[number | string,true,121]> // type H2 = string
5. 总结
通过以上例子有没发现infer 必须在 extends右侧使用,因为必须保证这个已知类型是由右侧的泛型推出来的,不然推导它的参数就没有了意义,检查时会跳过使用了 infer 的地方。