前一篇文章我們有介紹條件類型旭愧,這篇文章我們來介紹下非常實用的infer
的使用方法袄简。
使用介紹
我們上篇文章中介紹了條件類型的基本語法是:
T extends U ? X : Y;
如果占位符類型U
是一個可以被分解成幾個部分的類型逊桦,譬如數(shù)組類型谈喳,元組類型,函數(shù)類型钮孵,字符串字面量類型等骂倘。這時候可以通過infer
來獲取U
類型中某個部分的類型。
infer
語法的限制如下:
-
infer
只能在條件類型的 extends 子句中使用 -
infer
得到的類型只能在true
語句中使用, 即X
中使用
推斷數(shù)組(或者元組)的類型
使用方法
type InferArray<T> = T extends (infer U)[] ? U : never;
(infer U)
和平時常寫的string[]
巴席,number[]
等等是不是很像历涝?這里就是通過(infer U)
來獲取數(shù)組對應(yīng)的類型。
案例
type I0 = InferArray<[number, string]>; // string | number
type I1 = InferArray<string[]>; // string
type I2 = InferArray<number[]>; // number
推斷數(shù)組(或者元組)第一個元素的類型
使用方法
type InferFirst<T extends unknown[]> = T extends [infer P, ...infer _] ? P : never
[infer P, ... infer _]
中infer P獲取的
是第一個元素的類型,而...infer _
獲取的是數(shù)組其他剩余元素的數(shù)組類型;
特別說明下荧库,我們例子匯總不需要使用其他元素的類型诱担,所以用_
。
案例
type I3 = InferFirst<[3, 2, 1]>; // 3
推斷數(shù)組(或者元組)最后一個元素的類型
使用方法
type InferLast<T extends unknown[]> = T extends [... infer _, infer Last] ? Last : never;
這個和推斷數(shù)組第一個元素的類型類似电爹,
...infer _
獲取的是最后一個元素之前的所有元素類型,infer Last
獲取的是最后一個元素的類型料睛。
案例
type I4 = InferLast<[3, 2, 1]>; // 1
推斷函數(shù)類型的參數(shù)
使用方法
type InferParameters<T extends Function> = T extends (...args: infer R) => any ? R : never;
...args
代表的是函數(shù)參數(shù)組成的元組,infer R
代表的就是推斷出來的這個函數(shù)參數(shù)組成的元組的類型丐箩。
案例
type I5 = InferParameters<((arg1: string, arg2: number) => void)>; // [string, number]
推斷函數(shù)類型的返回值
使用方法
type InferReturnType<T extends Function> = T extends (...args: any) => infer R ? R : never;
和前面的
推斷函數(shù)類型的參數(shù)
類似,=>
后面的infer R
代表的就是推斷出來的函數(shù)的返回值類型恤煞。
案例
type I6 = InferReturnType<() => string>; // string
推斷Promise成功值的類型
使用方法
type InferPromise<T> = T extends Promise<infer U> ? U : never;
案例
type I7 = InferPromise<Promise<string>>; // string
推斷字符串字面量類型的第一個字符對應(yīng)的字面量類型
使用方法
type InferString<T extends string> = T extends `${infer First}${infer _}` ? First : [];
案例
type I8 = InferString<"Johnny">; // J
綜合案例
接下來我舉一些綜合性的例子屎勘,我不介紹這些例子實現(xiàn)的功能,大家來感受下infer
的使用技巧居扒,看看是否能一眼看出來實現(xiàn)的功能:
type Shift<T> = T extends [infer L, ...infer R]? [...R] : [];
type Pop<T extends any[]> = T extends [...infer L, infer R] ? [...L] : [];
type Reverse<T extends unknown[], U extends unknown[] = []> = [] extends T
? U
: T extends [infer L, ...infer R]
? Reverse<R, [L, ...U]>
: U;
type FlipArguments<T extends Function> = T extends (...arg: infer R) => infer S ? (...arg : Reverse<[...R]>) => S : T;
type StartsWith<T extends string, U extends string> = T extends `${U}${infer R}` ? true : false;
type TrimLeft<S extends string> = S extends `${infer L}${infer R}`
? L extends ' ' | '\n' | '\t'
? TrimLeft<R>
: S
: '';
type Trim<S extends string> = S extends `${' ' | '\t' | '\n'}${infer R}`
? Trim<R>
: S extends `${infer L}${' ' | '\t' | '\n'}`
? Trim<L>
: S;
type StringToUnion<T extends string, U = never> = T extends ''
? U
: T extends `${infer L}${infer R}`
? StringToUnion<R, U | L>
: U;
這些例子中涉及到兩個沒有介紹的知識點:模版字面量類型和遞歸類型概漱,如果對這兩個知識點不太懂的可以參考其他文章。這兩個知識點我后面也會介紹喜喂。