infer
infer
介绍
infer
表示在extends
条件语句中待推断的类型变量。示例如下:
type ParamType<T> = T extends (arg: infer P) => any ? P : T
interface User {
name: string
age: number
}
type Func = (user: User) => void
type Param1 = ParamType<Func> // User
type Param2 = ParamType<string> // string
提示
上面代码第一行中的T extends (arg: infer P) => any ? P : T
中的inter P
表示待推断的函数参数。
整个语句表达的意思是:传入的参数T
如果是继承自(arg: infer P) => any
,或者说T
能赋值给它,则返回类型P
(函数中的参数类型),否则返回传入的参数T
。
其他用法
提取函数类型的返回值类型
在上面的示例中,我们使用infer
获取的是函数类型中参数的类型,这里我们使用它获取返回值的类型。如下:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : T
type Func = (arg: any) => boolean
type R = ReturnType<Func> // boolean
可以看到,上面的这个代码只是将infer R
换到了=>
之后,即返回值的地方,这样我们就能获取到函数类型的返回值的类型了。
提取构造函数中参数或示例类型
构造函数一般都使用new
来实例化,因此它的类型通常表示如下:
type Constructor = new (...args: any[]) => any
因此,其实infer
在构造函数中使用时也有两个地方可以放,如:
- 参数处:
new (...args: infer P) => any
,这个其实就是获取的参数类型 - 返回值处:
new (...args: any[]) => infer R
,我们知道,构造函数一般表示的就是类本身,因此这个其实就是获取的实例类型,即类本身
示例如下:
// 获取参数类型
type ConstructorParameters<
T extends new (...args: any[]) => any
> = T extends new (...args: infer P) => any ? P : never
// 获取实例类型
type InstanceType<
T extends new (...args: any[]) => any
> = T extends new (...args: any[]) => infer R ? R : any
class TestClass {
constructor(public name: string, public age: number) {}
}
type Params = ConstructorParameters<typeof TestClass> // [string, number]
type Instance = InstanceType<typeof TestClass> // TestClass
提取数组元素的类型
如我们这里有个需求,一个类型如果传入的是数组,则获取数组的元素类型,否则返回传入的参数类型。
一般我们可以有如下写法:
// T[number] 表示返回数组中元素的类型,一般我们读取数组某个元素的方式都是 arr[1],这里就表示返回这个数组中所有元素的类型
type ReturnArrayType<T> = T extends Array<any> ? T[number] : T
type ArrA = (string | number)[]
type RAT = ReturnArrayType<ArrA> // string | number
type RAT1 = ReturnArrayType<string> // string
有了infer
后,我们就可以简化写法,如:
type ReturnArrayType<T> = T extends Array<infer P> ? P : T
type ArrA = (string | number)[]
type RAT = ReturnArrayType<ArrA> // string | number
type RAT1 = ReturnArrayType<string> // string
其实上面的type ArrA
就是一个元组类型,因此我们也可以看作它能将元组类型转换为联合类型:
type ReturnArrayType<T> = T extends Array<infer P> ? P : T
type TTuple = [string, number]
type RAT = ReturnArrayType<TTuple> // string | number
提取头部元素类型
type Arr = ["a", "b", "c"]
type GetFirst<T extends any[]> = T extends [infer First, ...any[]] ? First : []
type a = GetFirst<Arr> // "a"
提示
GetFirst
通过extends
约束T
只能是数组类型,然后通过infer
推断数组中第一个元素的类型,其他元素使用扩展运算符(...
)收集,最后返回。
提取尾部元素类型
与头部类型反过来即可。
type GetLast<T extends any[]> = T extends [...any[], infer Last] ? Last : []
剔除第一个元素
type Arr = ["a", "b", "c"]
type GetRest<T extends any[]> = T extends [unknown, ...infer Rest] ? Rest : []
type a = GetRest<Arr> // ["b", "c"]
剔除最后一个元素
与剔除第一个反过来即可。
type GetRest<T extends any[]> = T extends [...infer Rest, unknown] ? Rest : []
反转类型,递归
如我们有一个类型type Arr = ["a", "b", "c"]
,我们想让它变成["c", "b", "a"]
。
这样我们就可以使用递归:
type Arr = ["a", "b", "c"]
type Reverse<T extends any[]> = T extends [infer First, ...infer Rest] ? [...Reverse<Rest>, First] : []
type ReArr = Reverse<Arr> // ["c", "b", "a"]
提示
使用extends
约束T
的类型,然后使用infer
分别获取第一个元素和剩余其他元素,然后递归调用自己(Reverse<>
),并展开所有元素。
在递归调用自己后如果不展开的会得到如下类型: