跳到主要内容

条件类型和infer

条件类型

function universalAdd<T extends number | bigint | string>(x: T, y: T): LiteralToPrimitive<T> {
return x + (y as any);
}

export type LiteralToPrimitive<T> = T extends number ? number : T extends bigint ? bigint : T extends string ? string : never;

universalAdd('linbudu', '599'); // string
universalAdd(599, 1); // number
universalAdd(10n, 10n); // bigint

infer

infer 是 TypeScript 中用于条件类型中的类型推断的关键字。它允许你在泛型条件类型中声明一个待推断的类型变量。

基本概念

infer 只能在 extends 条件类型的子句中使用,用于提取类型的一部分。

type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

主要用途

1. 提取函数返回类型

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function foo() {
return 123;
}
type FooReturn = ReturnType<typeof foo>; // number

2. 提取函数参数类型

type Parameters<T> = T extends (...args: infer P) => any ? P : never;

function bar(x: string, y: number) {}
type BarParams = Parameters<typeof bar>; // [string, number]

3. 提取数组/元组元素类型

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

type NumArray = number[];
type Num = ElementType<NumArray>; // number

type StrArray = string[];
type Str = ElementType<StrArray>; // string

4. 提取 Promise 的解析类型

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type PromiseNumber = Promise<number>;
type NumberType = UnwrapPromise<PromiseNumber>; // number

type NotPromise = string;
type StringType = UnwrapPromise<NotPromise>; // string

高级用法

递归解构类型

// 提取多层 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U> ? DeepUnwrapPromise<U> : T;

type DeepPromise = Promise<Promise<number>>;
type DeepNumber = DeepUnwrapPromise<DeepPromise>; // number

提取构造函数实例类型

type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;

class MyClass {}
type MyInstance = InstanceType<typeof MyClass>; // MyClass

联合类型中的推断

// 提取所有函数的返回类型
type AllReturnTypes<T> = T extends any ? (T extends (...args: any[]) => infer R ? R : never) : never;

type Functions = [() => string, () => number];
type Returns = AllReturnTypes<Functions>; // string | number

实际应用示例

1. 路由参数提取

type ExtractRouteParams<T> = T extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractRouteParams<`${Rest}`> : T extends `${string}:${infer Param}` ? Param : never;

type Route = '/user/:id/post/:postId';
type Params = ExtractRouteParams<Route>; // "id" | "postId"

2. 响应数据处理

type ApiResponse<T> = {
data: T;
status: number;
};

type ExtractApiData<T> = T extends ApiResponse<infer U> ? U : never;

type UserResponse = ApiResponse<{ name: string; age: number }>;
type UserData = ExtractApiData<UserResponse>; // { name: string; age: number }

3. 递归类型处理

// 将嵌套数组展平
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;

type NestedArray = [1, [2, [3, 4]], 5];
type FlatArray = Flatten<NestedArray>; // 1 | 2 | 3 | 4 | 5

注意事项

  1. 只能在条件类型中使用infer 必须出现在 extends 的右侧

  2. 类型推断是惰性的:只有在实际使用时才会进行推断

  3. 多个 infer 位置:可以在一个条件类型中使用多个 infer

    type FirstSecond<T> = T extends [infer First, infer Second, ...any[]] ? [First, Second] : never;
  4. 推断失败的情况:如果推断失败,会回退到 never 或指定的默认类型

infer 是 TypeScript 类型编程中非常强大的工具,它让你能够编写更加灵活和通用的类型工具。