介绍
TypeScript 的类型系统是图灵完备,而释放其全部功能的关键功能之一是条件类型。就像 JavaScript 中的三元运算符 (condition ? a : b) 一样,条件类型可让您创建类型级逻辑:T extends U ? X : Y。这使得动态的、上下文感知的类型解析成为可能。
基本语法
条件类型检查一个类型是否扩展另一个类型:
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<42>; // false
条件 T extends U 询问:“T 是否可以分配给 U?”如果是,则解析到true分支;否则,为假分支。
infer 关键字
infer 关键字允许您从条件中提取类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet() { return 'hello'; }
type G = ReturnType<typeof greet>; // string
infer R 声明一个从匹配位置推断出的类型变量 R。这是许多实用程序类型的基础。
分配条件类型
当条件类型与 union 一起使用时,它会分布在每个成员上:
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;
// string[] | number[] (not (string | number)[])
除非您将两侧包裹起来,否则这种分布会自动发生:
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result2 = ToArrayNonDist<string | number>;
// (string | number)[]
使用条件的内置实用程序类型
许多标准实用程序类型依赖于条件类型:
// Exclude — removes types from a union
type T0 = Exclude<'a' | 'b' | 'c', 'a'>;
// 'b' | 'c'
// Extract — keeps only matching types
type T1 = Extract<string | number | (() => void), Function>;
// () => void
// NonNullable — removes null and undefined
type T2 = NonNullable<string | number | null | undefined>;
// string | number
以下是 Exclude 的实现方式:
type MyExclude<T, U> = T extends U ? never : T;
现实世界的模式
基于输入的条件返回类型
type AsyncResult<T> = T extends Promise<infer U> ? U : T;
async function fetchData(): Promise<string> {
return 'data';
}
type Data = AsyncResult<ReturnType<typeof fetchData>>; // string
函数重载 无重载
type StringOrNumber<T> = T extends string ? string : number;
function process<T extends string | number>(val: T): StringOrNumber<T>;
function process(val: any): any {
return typeof val === 'string' ? val.toUpperCase() : val * 10;
}
const a = process('hello'); // string
const b = process(42); // number
深层条件类型示例
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
interface User {
name: string;
address: { city: string; zip: number };
}
type ReadonlyUser = DeepReadonly<User>;
// { readonly name: string; readonly address: { readonly city: string; readonly zip: number } }
结论
条件类型是类型系统的 if/else。与 infer、分布和映射类型相结合,它们可以实现复杂的类型转换,从而在编译时捕获错误。掌握条件来编写真正类型安全、自记录的 TypeScript 代码。

