条件类型
概述
条件类型(Conditional Types)是 TypeScript 2.8 引入的强大特性,它允许我们根据类型条件来选择不同的类型。条件类型使用三元运算符 T extends U ? X : Y 的语法,类似于 JavaScript 中的三元表达式,但作用于类型层面。通过条件类型,我们可以创建更加灵活和智能的类型系统,实现类型级别的条件判断和类型转换。
为什么需要条件类型
在没有条件类型的情况下,我们无法根据类型关系动态选择类型:
typescript
// 没有条件类型:无法根据类型关系选择不同的类型
type GetType<T> = T; // 只能返回 T 本身
// 使用条件类型:可以根据类型关系选择不同的类型
type GetType<T> = T extends string ? string : number;条件类型让我们能够:
- 根据类型关系选择类型:根据一个类型是否满足某个条件,选择不同的类型
- 实现类型转换:将一种类型转换为另一种类型
- 创建类型工具:构建复杂的类型工具和类型操作
- 类型级编程:在类型层面实现逻辑判断
基本语法
基本条件类型
条件类型使用 extends 关键字和三元运算符 ? ::
typescript
// 基本语法:T extends U ? X : Y
// 如果 T 可以赋值给 U,则类型为 X,否则为 Y
type IsString<T> = T extends string ? true : false;
// 使用示例
type Test1 = IsString<string>; // 类型:true
type Test2 = IsString<number>; // 类型:false
type Test3 = IsString<"hello">; // 类型:true(字面量类型是 string 的子类型)条件类型的工作原理
条件类型通过检查类型 T 是否可以赋值给类型 U 来判断条件:
typescript
// 检查 T 是否为数组类型
type IsArray<T> = T extends any[] ? true : false;
// 使用示例
type Test1 = IsArray<number[]>; // 类型:true
type Test2 = IsArray<string[]>; // 类型:true
type Test3 = IsArray<number>; // 类型:false
type Test4 = IsArray<[number, string]>; // 类型:true(元组也是数组)嵌套条件类型
条件类型可以嵌套,实现更复杂的类型判断:
typescript
// 嵌套条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends null ? "null" :
T extends Function ? "function" :
"object";
// 使用示例
type Test1 = TypeName<string>; // 类型:"string"
type Test2 = TypeName<number>; // 类型:"number"
type Test3 = TypeName<boolean>; // 类型:"boolean"
type Test4 = TypeName<undefined>; // 类型:"undefined"
type Test5 = TypeName<null>; // 类型:"null"
type Test6 = TypeName<() => void>; // 类型:"function"
type Test7 = TypeName<{}>; // 类型:"object"分布式条件类型
当条件类型作用于联合类型时,TypeScript 会进行分布式条件类型(Distributive Conditional Types)处理:
分布式行为
typescript
// 条件类型作用于联合类型时,会分布到每个成员
type ToArray<T> = T extends any ? T[] : never;
// 使用示例
type Test = ToArray<string | number>;
// 等价于:ToArray<string> | ToArray<number>
// 结果:string[] | number[]实际应用
typescript
// 提取函数类型的返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 使用示例
type Func1 = () => string;
type Func2 = () => number;
type Func3 = () => boolean;
type Return1 = ReturnType<Func1>; // 类型:string
type Return2 = ReturnType<Func2>; // 类型:number
type Return3 = ReturnType<Func3>; // 类型:boolean
type Return4 = ReturnType<Func1 | Func2>; // 类型:string | number(分布式)阻止分布式行为
使用方括号包裹类型参数可以阻止分布式行为:
typescript
// 阻止分布式:使用 [T] 包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
// 对比
type Test1 = ToArray<string | number>;
// 类型:string[] | number[](分布式)
type Test2 = ToArrayNonDist<string | number>;
// 类型:(string | number)[](非分布式)使用 infer 操作符
infer 操作符用于在条件类型中推断类型,它是条件类型最强大的特性之一:
基本用法
typescript
// 使用 infer 推断函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 使用示例
type Func = () => string;
type Return = GetReturnType<Func>; // 类型:string
type AsyncFunc = () => Promise<number>;
type AsyncReturn = GetReturnType<AsyncFunc>; // 类型:Promise<number>推断函数参数类型
typescript
// 推断函数参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;
// 使用示例
type Func1 = (a: string, b: number) => void;
type Params1 = GetParameters<Func1>; // 类型:[string, number]
type Func2 = () => void;
type Params2 = GetParameters<Func2>; // 类型:[]推断数组元素类型
typescript
// 推断数组元素类型
type GetArrayElement<T> = T extends (infer E)[] ? E : never;
// 使用示例
type Test1 = GetArrayElement<string[]>; // 类型:string
type Test2 = GetArrayElement<number[]>; // 类型:number
type Test3 = GetArrayElement<[string, number]>; // 类型:string | number(元组)推断 Promise 类型
typescript
// 推断 Promise 的解析类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
// 使用示例
type Test1 = UnwrapPromise<Promise<string>>; // 类型:string
type Test2 = UnwrapPromise<Promise<number>>; // 类型:number
type Test3 = UnwrapPromise<string>; // 类型:string(非 Promise)多个 infer 推断
typescript
// 同时推断多个类型
type GetFirstAndRest<T> = T extends [infer First, ...infer Rest]
? { first: First; rest: Rest }
: never;
// 使用示例
type Test1 = GetFirstAndRest<[string, number, boolean]>;
// 类型:{ first: string; rest: [number, boolean] }
type Test2 = GetFirstAndRest<[string]>;
// 类型:{ first: string; rest: [] }使用示例
示例 1:类型工具函数
typescript
// 提取函数返回类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 提取函数参数类型
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
// 提取构造函数参数类型
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
// 提取构造函数实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never;
// 使用示例
function createUser(name: string, age: number): { name: string; age: number } {
return { name, age };
}
type UserReturn = MyReturnType<typeof createUser>;
// 类型:{ name: string; age: number }
type UserParams = MyParameters<typeof createUser>;
// 类型:[string, number]
class User {
constructor(public name: string, public age: number) {}
}
type UserConstructorParams = ConstructorParameters<typeof User>;
// 类型:[string, number]
type UserInstance = InstanceType<typeof User>;
// 类型:User示例 2:类型过滤
typescript
// 过滤联合类型中的特定类型
type FilterString<T> = T extends string ? T : never;
// 使用示例
type Test = FilterString<string | number | boolean | string>;
// 类型:string(过滤掉非 string 类型)
// 过滤函数类型
type FilterFunction<T> = T extends Function ? T : never;
type Test2 = FilterFunction<string | number | (() => void) | boolean>;
// 类型:() => void示例 3:类型转换
typescript
// 将联合类型转换为交叉类型
type UnionToIntersection<U> =
(U extends any ? (x: U) => void : never) extends (x: infer I) => void
? I
: never;
// 使用示例
type Test = UnionToIntersection<{ a: string } | { b: number }>;
// 类型:{ a: string } & { b: number }
// 将可选属性变为必需属性
type RequiredKeys<T> = {
[K in keyof T]-?: T[K];
};
// 将只读属性变为可写属性
type Writable<T> = {
-readonly [K in keyof T]: T[K];
};示例 4:深度解包 Promise
typescript
// 深度解包嵌套的 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U>
? DeepUnwrapPromise<U>
: T;
// 使用示例
type Test1 = DeepUnwrapPromise<Promise<string>>;
// 类型:string
type Test2 = DeepUnwrapPromise<Promise<Promise<number>>>;
// 类型:number
type Test3 = DeepUnwrapPromise<Promise<Promise<Promise<boolean>>>>;
// 类型:boolean示例 5:类型守卫工具
typescript
// 检查类型是否为 never
type IsNever<T> = [T] extends [never] ? true : false;
// 检查类型是否为 any
type IsAny<T> = 0 extends 1 & T ? true : false;
// 检查类型是否为 unknown
type IsUnknown<T> = IsNever<T> extends false
? IsAny<T> extends false
? unknown extends T
? true
: false
: false
: false;
// 使用示例
type Test1 = IsNever<never>; // 类型:true
type Test2 = IsNever<string>; // 类型:false
type Test3 = IsAny<any>; // 类型:true
type Test4 = IsAny<string>; // 类型:false示例 6:API 响应类型处理
typescript
// 根据状态类型选择不同的响应类型
type ApiResponse<T> =
T extends { status: "success" }
? { status: "success"; data: T["data"] }
: T extends { status: "error" }
? { status: "error"; message: string }
: never;
// 使用示例
type SuccessResponse = ApiResponse<{
status: "success";
data: { id: number; name: string };
}>;
// 类型:{ status: "success"; data: { id: number; name: string } }
type ErrorResponse = ApiResponse<{
status: "error";
message: string;
}>;
// 类型:{ status: "error"; message: string }示例 7:数组操作类型
typescript
// 获取数组的第一个元素类型
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
// 获取数组的最后一个元素类型
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
// 获取数组除第一个元素外的类型
type Tail<T extends any[]> = T extends [any, ...infer Rest] ? Rest : never;
// 获取数组除最后一个元素外的类型
type Head<T extends any[]> = T extends [...infer Rest, any] ? Rest : never;
// 使用示例
type Test1 = First<[string, number, boolean]>; // 类型:string
type Test2 = Last<[string, number, boolean]>; // 类型:boolean
type Test3 = Tail<[string, number, boolean]>; // 类型:[number, boolean]
type Test4 = Head<[string, number, boolean]>; // 类型:[string, number]条件类型与工具类型
条件类型是构建 TypeScript 内置工具类型的基础:
Exclude 类型
typescript
// Exclude:从联合类型中排除特定类型
type MyExclude<T, U> = T extends U ? never : T;
// 使用示例
type Test = MyExclude<string | number | boolean, number>;
// 类型:string | booleanExtract 类型
typescript
// Extract:从联合类型中提取特定类型
type MyExtract<T, U> = T extends U ? T : never;
// 使用示例
type Test = MyExtract<string | number | boolean, number | string>;
// 类型:string | numberNonNullable 类型
typescript
// NonNullable:排除 null 和 undefined
type MyNonNullable<T> = T extends null | undefined ? never : T;
// 使用示例
type Test = MyNonNullable<string | number | null | undefined>;
// 类型:string | number类型检查示例
常见错误
typescript
// ❌ 错误:条件类型语法错误
// type Wrong = T extends U X : Y; // 缺少 ? 和 :
// ❌ 错误:infer 只能在条件类型的 true 分支使用
// type Wrong<T> = T extends any ? infer R : never; // infer 位置错误
// ❌ 错误:条件类型判断逻辑错误
type IsArray<T> = T extends any[] ? true : false;
type Test = IsArray<never>; // 类型:never(分布式条件类型的行为)正确写法
typescript
// ✅ 正确:基本条件类型
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // 类型:true
type Test2 = IsString<number>; // 类型:false
// ✅ 正确:使用 infer 推断类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Func = () => string;
type Return = GetReturnType<Func>; // 类型:string
// ✅ 正确:阻止分布式行为
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Test = ToArrayNonDist<string | number>;
// 类型:(string | number)[](非分布式)
// ✅ 正确:嵌套条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
"other";注意事项
提示
- 条件类型使用
extends关键字检查类型关系,类似于类型约束 infer操作符用于在条件类型中推断类型,只能在条件类型的 true 分支使用- 条件类型作用于联合类型时会进行分布式处理,每个成员都会单独判断
- 使用方括号
[T]包裹类型参数可以阻止分布式行为 - 条件类型是构建复杂类型工具的基础,与泛型、映射类型结合使用更强大
注意
- 条件类型的判断基于类型关系,不是值的关系
- 分布式条件类型可能会产生意外的结果,需要理解其行为
- 复杂的条件类型可能会影响类型检查性能
infer操作符只能在条件类型的 true 分支使用,不能在 false 分支使用- 条件类型中的类型推断是延迟的,只有在实际使用时才会计算
重要
- 条件类型是 TypeScript 类型级编程的核心特性,理解条件类型对于掌握高级类型非常重要
infer操作符是条件类型最强大的特性,可以推断函数参数、返回类型、数组元素等- 分布式条件类型是条件类型的重要特性,理解其行为对于正确使用条件类型至关重要
- 条件类型与泛型约束、映射类型、工具类型结合使用,可以构建强大的类型系统
信息
条件类型的优势:
- 类型级条件判断:在类型层面实现条件逻辑
- 类型转换:将一种类型转换为另一种类型
- 类型推断:通过
infer推断复杂类型的组成部分 - 类型工具构建:构建可复用的类型工具
条件类型的应用场景:
- 工具类型实现(Exclude、Extract、NonNullable 等)
- 函数类型操作(参数类型、返回类型提取)
- 类型转换和映射
- 复杂类型系统的构建
- 框架和库的类型定义