TS進階之infer

語言: CN / TW / HK

前一篇文章我們有介紹條件類型,這篇文章我們來介紹下非常實用的infer的使用方法。

使用介紹

我們上篇文章中介紹了條件類型的基本語法是: T extends U ? X : Y;

如果佔位符類型U是一個可以被分解成幾個部分的類型,譬如數組類型,元組類型,函數類型,字符串字面量類型等。這時候可以通過infer來獲取U類型中某個部分的類型。

infer語法的限制如下: 1. infer只能在條件類型的 extends 子句中使用 2. infer得到的類型只能在true語句中使用, 即X中使用

推斷數組(或者元組)的類型

使用方法

type InferArray<T> = T extends (infer U)[] ? U : never;

(infer U)和平時常寫的string[]number[]等等是不是很像?這裏就是通過(infer U)來獲取數組對應的類型。

案例

type I0 = InferArray<[number, string]>; // string | number type I1 = InferArray<string[]>; // string type I2 = InferArray<number[]>; // number

推斷數組(或者元組)第一個元素的類型

使用方法

type InferFirst<T extends unknown[]> = T extends [infer P, ...infer _] ? P : never

[infer P, ... infer _]infer P獲取的是第一個元素的類型,而...infer _獲取的是數組其他剩餘元素的數組類型; 特別説明下,我們例子彙總不需要使用其他元素的類型,所以用_

案例

type I3 = InferFirst<[3, 2, 1]>; // 3

推斷數組(或者元組)最後一個元素的類型

使用方法

type InferLast<T extends unknown[]> = T extends [... infer _, infer Last] ? Last : never;

這個和推斷數組第一個元素的類型類似,...infer _獲取的是最後一個元素之前的所有元素類型,infer Last獲取的是最後一個元素的類型。

案例

type I4 = InferLast<[3, 2, 1]>; // 1

推斷函數類型的參數

使用方法

type InferParameters<T extends Function> = 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 Function> = 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<Promise<string>>; // string

推斷字符串字面量類型的第一個字符對應的字面量類型

使用方法

type InferString<T extends string> = T extends `${infer First}${infer _}` ? First : [];

案例

type I8 = InferString<"Johnny">; // J

綜合案例

接下來我舉一些綜合性的例子,我不介紹這些例子實現的功能,大家來感受下infer的使用技巧,看看是否能一眼看出來實現的功能:

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;

這些例子中涉及到兩個沒有介紹的知識點:模版字面量類型遞歸類型,如果對這兩個知識點不太懂的可以參考其他文章。這兩個知識點我後面也會介紹。

「其他文章」