分布式条件类型

分布式条件类型(Distributive Conditional Types)是TypeScript中一种特殊的高级类型特性,它在处理联合类型时表现出独特的"分布式"行为。当条件类型作用于联合类型时,TypeScript会自动将该条件类型"分发"到联合类型的每个成员上。

typescript 复制代码
type Distributed<T> = T extends any ? T[] : never;
type Result = Distributed<string | number>; // string[] | number[]

分布式行为的工作原理

理解分布式条件类型的核心在于掌握它的触发条件:

  1. 类型参数是裸类型参数:即类型参数没有被其他类型构造器包裹(如数组、元组、函数等)
  2. 类型参数出现在extends关键字左侧
  3. 被检查的类型是联合类型

当这些条件满足时,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>;

避免分布式行为

有时我们可能不希望条件类型表现出分布式行为,可以通过以下方式阻止:

  1. 将类型参数包装在元组中

    typescript 复制代码
    type NoDistribute<T> = [T] extends [any] ? T[] : never;
    type Result = NoDistribute<string | number>; // (string | number)[]
  2. 使用never类型

    typescript 复制代码
    type 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];

常见陷阱与解决方案

  1. 意外的分布式行为

    • 问题:忘记条件类型在联合类型上的分布式特性
    • 解决:明确是否需要分布式行为,必要时使用包装技巧
  2. 循环引用

    • 问题:递归类型可能导致无限循环
    • 解决:添加终止条件或限制递归深度
  3. 性能问题

    • 问题:复杂类型操作可能导致编译速度下降
    • 解决:简化类型逻辑或拆分复杂类型

总结

分布式条件类型是TypeScript类型系统中一个强大而独特的特性,它使得类型操作更加灵活和精确。掌握这一特性可以让你:

  • 更精确地操作和转换联合类型
  • 构建更复杂的类型工具和实用类型
  • 编写更具表现力的类型定义
  • 处理各种边界情况和复杂场景

通过合理运用分布式条件类型,你可以将TypeScript的类型系统能力发挥到极致,为项目带来更强大的类型安全保障。