前一篇文章我们有介绍条件类型,这篇文章我们来介绍下非常实用的infer
的使用方法。
使用介绍
我们上篇文章中介绍了条件类型的基本语法是:
T extends U ? X : Y;
如果占位符类型U
是一个可以被分解成几个部分的类型,譬如数组类型,元组类型,函数类型,字符串字面量类型等。这时候可以通过infer
来获取U
类型中某个部分的类型。
infer
语法的限制如下:
-
infer
只能在条件类型的 extends 子句中使用 -
infer
得到的类型只能在true
语句中使用, 即X
中使用
推断数组(或者元组)的类型
使用方法
type InferArray = T extends (infer U)[] ? U : never;
(infer U)
和平时常写的string[]
,number[]
等等是不是很像?这里就是通过(infer U)
来获取数组对应的类型。
案例
type I0 = InferArray<[number, string]>; // string | number
type I1 = InferArray; // string
type I2 = InferArray; // number
推断数组(或者元组)第一个元素的类型
使用方法
type InferFirst = T extends [infer P, ...infer _] ? P : never
[infer P, ... infer _]
中infer P获取的
是第一个元素的类型,而...infer _
获取的是数组其他剩余元素的数组类型;
特别说明下,我们例子汇总不需要使用其他元素的类型,所以用_
。
案例
type I3 = InferFirst<[3, 2, 1]>; // 3
推断数组(或者元组)最后一个元素的类型
使用方法
type InferLast = T extends [... infer _, infer Last] ? Last : never;
这个和推断数组第一个元素的类型类似,
...infer _
获取的是最后一个元素之前的所有元素类型,infer Last
获取的是最后一个元素的类型。
案例
type I4 = InferLast<[3, 2, 1]>; // 1
推断函数类型的参数
使用方法
type InferParameters = T extends (...args: infer R) => any ? R : never;
...args
代表的是函数参数组成的元组,infer R
代表的就是推断出来的这个函数参数组成的元组的类型。
案例
type I5 = InferParameters<((arg1: string, arg2: number) => void)>; // [string, number]
推断函数类型的返回值
使用方法
type InferReturnType = T extends (...args: any) => infer R ? R : never;
和前面的
推断函数类型的参数
类似,=>
后面的infer R
代表的就是推断出来的函数的返回值类型。
案例
type I6 = InferReturnType<() => string>; // string
推断Promise成功值的类型
使用方法
type InferPromise = T extends Promise ? U : never;
案例
type I7 = InferPromise>; // string
推断字符串字面量类型的第一个字符对应的字面量类型
使用方法
type InferString = T extends `${infer First}${infer _}` ? First : [];
案例
type I8 = InferString<"Johnny">; // J
综合案例
接下来我举一些综合性的例子,我不介绍这些例子实现的功能,大家来感受下infer
的使用技巧,看看是否能一眼看出来实现的功能:
type Shift = T extends [infer L, ...infer R]? [...R] : [];
type Pop = T extends [...infer L, infer R] ? [...L] : [];
type Reverse = [] extends T
? U
: T extends [infer L, ...infer R]
? Reverse
: U;
type FlipArguments = T extends (...arg: infer R) => infer S ? (...arg : Reverse<[...R]>) => S : T;
type StartsWith = T extends `${U}${infer R}` ? true : false;
type TrimLeft = S extends `${infer L}${infer R}`
? L extends ' ' | '\n' | '\t'
? TrimLeft
: S
: '';
type Trim = S extends `${' ' | '\t' | '\n'}${infer R}`
? Trim
: S extends `${infer L}${' ' | '\t' | '\n'}`
? Trim
: S;
type StringToUnion = T extends ''
? U
: T extends `${infer L}${infer R}`
? StringToUnion
: U;
这些例子中涉及到两个没有介绍的知识点:模版字面量类型和递归类型,如果对这两个知识点不太懂的可以参考其他文章。这两个知识点我后面也会介绍。