分布式条件类型(Distributive Conditional Types)是TypeScript中一种特殊的高级类型特性,它在处理联合类型时表现出独特的"分布式"行为。当条件类型作用于联合类型时,TypeScript会自动将该条件类型"分发"到联合类型的每个成员上。
typescript
type Distributed<T> = T extends any ? T[] : never;
type Result = Distributed<string | number>; // string[] | number[]
分布式行为的工作原理
理解分布式条件类型的核心在于掌握它的触发条件:
- 类型参数是裸类型参数:即类型参数没有被其他类型构造器包裹(如数组、元组、函数等)
- 类型参数出现在
extends
关键字左侧 - 被检查的类型是联合类型
当这些条件满足时,TypeScript会将条件类型"分发"到联合类型的每个成员上:
typescript
type ToArray<T> = T extends any ? T[] : never;
type StrArrOrNumArr = ToArray<string | number>;
// 等价于
type StrArrOrNumArr = ToArray<string> | ToArray<number>;
// 最终得到 string[] | number[]
实际应用场景
1. 过滤联合类型
typescript
type Filter<T, U> = T extends U ? T : never;
type Filtered = Filter<string | number | boolean, number | boolean>;
// 结果为:number | boolean
2. 提取函数返回类型
typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type FnReturn = ReturnType<(() => string) | (() => number)>;
// 结果为:string | number
3. 排除特定类型
typescript
type Exclude<T, U> = T extends U ? never : T;
type NonNullable<T> = Exclude<T, null | undefined>;
避免分布式行为
有时我们可能不希望条件类型表现出分布式行为,可以通过以下方式阻止:
-
将类型参数包装在元组中:
typescripttype NoDistribute<T> = [T] extends [any] ? T[] : never; type Result = NoDistribute<string | number>; // (string | number)[]
-
使用
never
类型:typescripttype NoDistribute<T> = T & {} extends any ? T[] : never;
高级技巧与模式
递归类型处理
typescript
type DeepReadonly<T> = T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
类型谓词与映射类型结合
typescript
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
常见陷阱与解决方案
-
意外的分布式行为:
- 问题:忘记条件类型在联合类型上的分布式特性
- 解决:明确是否需要分布式行为,必要时使用包装技巧
-
循环引用:
- 问题:递归类型可能导致无限循环
- 解决:添加终止条件或限制递归深度
-
性能问题:
- 问题:复杂类型操作可能导致编译速度下降
- 解决:简化类型逻辑或拆分复杂类型
总结
分布式条件类型是TypeScript类型系统中一个强大而独特的特性,它使得类型操作更加灵活和精确。掌握这一特性可以让你:
- 更精确地操作和转换联合类型
- 构建更复杂的类型工具和实用类型
- 编写更具表现力的类型定义
- 处理各种边界情况和复杂场景
通过合理运用分布式条件类型,你可以将TypeScript的类型系统能力发挥到极致,为项目带来更强大的类型安全保障。