11 - 条件类型
条件类型
条件类型(Conditional Types)是 TypeScript 类型系统中最强大的特性之一,它允许根据类型条件进行类型推断和变换。
基本语法
条件类型的语法类似三元表达式:
// 基本条件类型
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
type C = IsString<"hello">; // "yes"
条件类型的结构
T extends U ? X : Y
│ │ │
│ │ └─ 否则返回 Y
│ └─ 那么返回 X
└─ 如果 T 可以赋值给 U
分布式条件类型(Distributive Conditional Types)
当条件类型作用于联合类型时,会自动分发:
type ToArray<T> = T extends any ? T[] : never;
// 对联合类型的每个成员分别应用
type Result = ToArray<string | number>;
// 等价于 ToArray<string> | ToArray<number>
// = string[] | number[]
// 而不是 (string | number)[]
禁止分发
// 使用 [] 包裹禁止分发
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result2 = ToArrayNonDist<string | number>;
// (string | number)[]
infer 关键字
infer 用于在条件类型中声明待推断的类型变量:
推断函数返回值类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = MyReturnType<() => string>; // string
type B = MyReturnType<(x: number) => boolean>; // boolean
type C = MyReturnType<string>; // never
推断函数参数类型
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
type A = MyParameters<(a: string, b: number) => void>;
// [a: string, b: number]
推断 Promise 内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<Promise<Promise<number>>>; // Promise<number>(只解一层)
type C = UnwrapPromise<string>; // string
// 递归解包
type DeepUnwrapPromise<T> = T extends Promise<infer U>
? DeepUnwrapPromise<U>
: T;
type D = DeepUnwrapPromise<Promise<Promise<Promise<string>>>>; // string
推断数组元素类型
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type A = ArrayElement<string[]>; // string
type B = ArrayElement<(string | number)[]>; // string | number
type C = ArrayElement<[string, number]>; // string | number
推断元组首尾元素
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never;
type Tail<T extends any[]> = T extends [any, ...infer T] ? T : never;
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type A = Head<[string, number, boolean]>; // string
type B = Tail<[string, number, boolean]>; // [number, boolean]
type C = Last<[string, number, boolean]>; // boolean
推断构造函数实例类型
type InstanceOf<T> = T extends new (...args: any[]) => infer I ? I : never;
class User {
name: string = "";
}
type UserInstance = InstanceOf<typeof User>; // User
复杂推断示例
推断模板字面量类型
type ExtractRouteParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractRouteParams<Rest>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractRouteParams<"/users/:userId/posts/:postId">;
// "userId" | "postId"
推断对象值类型
type ValueOf<T> = T[keyof T];
type User = {
name: string;
age: number;
email: string;
};
type UserValue = ValueOf<User>; // string | number
递归类型
递归深度限制
// 递归 DeepPartial
type DeepPartial<T> = T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;
interface Config {
a: {
b: {
c: string;
};
d: number;
};
e: boolean;
}
type PartialConfig = DeepPartial<Config>;
// {
// a?: {
// b?: { c?: string };
// d?: number;
// };
// e?: boolean;
// }
递归 DeepReadonly
type DeepReadonly<T> = T extends (infer U)[]
? ReadonlyArray<DeepReadonly<U>>
: T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;
递归元组操作
// 反转元组
type Reverse<T extends any[]> = T extends [infer H, ...infer Rest]
? [...Reverse<Rest>, H]
: [];
type A = Reverse<[1, 2, 3]>; // [3, 2, 1]
// 连接元组
type Concat<A extends any[], B extends any[]> = [...A, ...B];
type B = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]
// Flatten 元组
type Flatten<T extends any[]> = T extends [infer H, ...infer Rest]
? H extends any[]
? [...Flatten<H>, ...Flatten<Rest>]
: [H, ...Flatten<Rest>]
: [];
type C = Flatten<[[1, 2], [3, [4, 5]]]>; // [1, 2, 3, 4, 5]
递归字符串操作
// 字符串转联合类型
type StringToUnion<S extends string> =
S extends `${infer C}${infer Rest}`
? C | StringToUnion<Rest>
: never;
type Letters = StringToUnion<"hello">; // "h" | "e" | "l" | "o"
// 驼峰转连字符
type CamelToKebab<S extends string> =
S extends `${infer C}${infer Rest}`
? Rest extends `${infer R}${string}`
? R extends Uppercase<R>
? `${Lowercase<C>}-${CamelToKebab<Rest>}`
: `${C}${CamelToKebab<Rest>}`
: C
: S;
type Kebab = CamelToKebab<"backgroundColor">; // "background-color"
递归数字类型
// 构建元组表示数字
type BuildTuple<N extends number, T extends any[] = []> =
T["length"] extends N ? T : BuildTuple<N, [...T, any]>;
// 加法
type Add<A extends number, B extends number> = [
...BuildTuple<A>,
...BuildTuple<B>
]["length"] & number;
type Sum = Add<2, 3>; // 5
条件类型与映射类型结合
// 根据条件过滤属性
type FilterByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface User {
id: number;
name: string;
age: number;
email: string;
active: boolean;
}
type StringProps = FilterByType<User, string>;
// { name: string; email: string }
type NumberProps = FilterByType<User, number>;
// { id: number; age: number }
// 提取可选属性的键
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
// 提取必选属性的键
type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
条件类型推断的实战应用
事件处理类型
interface EventMap {
click: { x: number; y: number };
change: { value: string };
submit: { data: Record<string, any> };
}
type EventHandler<K extends keyof EventMap> = (
event: EventMap[K]
) => void;
type EventHandlers = {
[K in keyof EventMap]: EventHandler<K>;
};
// 或使用条件类型
type EventHandlerFor<T> = T extends keyof EventMap
? (event: EventMap[T]) => void
: never;
API 路由类型推断
// 路由定义
interface ApiRoutes {
"/users": {
GET: { response: User[] };
POST: { body: CreateUserDto; response: User };
};
"/users/:id": {
GET: { response: User };
PUT: { body: UpdateUserDto; response: User };
DELETE: { response: void };
};
}
// 推断响应类型
type ApiResponse<
Path extends keyof ApiRoutes,
Method extends keyof ApiRoutes[Path]
> = ApiRoutes[Path][Method] extends { response: infer R } ? R : never;
type UsersResponse = ApiResponse<"/users", "GET">; // User[]
type UserResponse = ApiResponse<"/users/:id", "GET">; // User
业务场景:类型安全的 ORM 查询
// 字段类型映射
interface ModelFields {
User: {
id: number;
name: string;
email: string;
age: number;
active: boolean;
createdAt: Date;
};
}
// 查询条件类型
type WhereCondition<T> = {
[K in keyof T]?: T[K] | T[K][] | {
eq?: T[K];
in?: T[K][];
gt?: T[K] extends number ? T[K] : never;
lt?: T[K] extends number ? T[K] : never;
like?: T[K] extends string ? string : never;
};
};
// 类型安全的查询
type UserWhere = WhereCondition<ModelFields["User"]>;
const where: UserWhere = {
name: { like: "%Alice%" },
age: { gt: 18 },
active: true
};
注意事项
- 分布式条件类型:联合类型会自动分发,需要时用
[]包裹禁止 infer只能在条件类型的extends子句中使用- 递归类型注意深度限制——TypeScript 有递归深度限制(约 50 层)
- 条件类型可以用于创建复杂的类型变换——但不要过度复杂化
- 联合类型的分发顺序:先分发再求值