条件类型和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
注意事项
只能在条件类型中使用:
infer
必须出现在extends
的右侧类型推断是惰性的:只有在实际使用时才会进行推断
多个 infer 位置:可以在一个条件类型中使用多个
infer
type FirstSecond<T> = T extends [infer First, infer Second, ...any[]] ? [First, Second] : never;
推断失败的情况:如果推断失败,会回退到
never
或指定的默认类型
infer
是 TypeScript 类型编程中非常强大的工具,它让你能够编写更加灵活和通用的类型工具。