在 TypeScript 中,条件类型具有一种特殊的行为:当泛型参数是联合类型时,条件类型会对联合类型的每个成员逐一应用条件,而不是整体作为一个类型处理。这种行为称为 **分布式条件类型(distributive conditional types)**。

条件类型的语法

T extends U ? X : Y

这里,T 是被判断的类型,U 是条件类型的基准。如果 TU 的子类型,那么返回 X,否则返回 Y

分布式特性

分布式特性只有在 T 是一个 裸露的泛型类型(裸类型参数) 时才会生效。例如:

type Distributed<T> = T extends U ? X : Y;

如果 T 是一个联合类型,如 A | B | C,TypeScript 会自动将其分解成 ABC,分别代入条件类型计算。


结合原代码分析

UnionToIntersection 的定义中:

T extends any ? (x: T) => any : never
  • 这里的 T 可能是一个联合类型,例如 { a: string } | { b: number }
  • T extends any 触发了分布式条件类型特性。
  • 因此,对于联合类型 T,TypeScript 会将其分解为 { a: string }{ b: number },然后分别计算条件类型。

这使得 T 被遍历为联合类型的每一个成员,而不是整体作为一个类型。


具体执行过程

假设 T{ a: string } | { b: number }

  1. TypeScript 会将 T 的联合类型拆分:

    • 第一次计算,T 被代入为 { a: string }
      (x: { a: string }) => any
      
    • 第二次计算,T 被代入为 { b: number }
      (x: { b: number }) => any
      
  2. 这两次计算的结果会合并成一个新的联合类型:

    (x: { a: string }) => any | (x: { b: number }) => any
    

为什么这样设计?

TypeScript 的分布式特性在处理联合类型时极为灵活。通过 T extends any,我们可以拆分并单独操作联合类型的每个成员。配合推断 infer 和交叉类型操作,我们可以将这些单独的结果重新组合成想要的类型。


进一步示例

以下代码更直观地展示了分布式条件类型的作用:

type Example<T> = T extends any ? { value: T } : never;

// 如果传入一个联合类型
type Result = Example<string | number>;
// TypeScript 会分布计算:
// - 当 T = string 时,结果为 { value: string }
// - 当 T = number 时,结果为 { value: number }
// 最终 Result 为 { value: string } | { value: number }

这种机制也是 UnionToIntersection 能够逐个处理联合类型并最终生成交叉类型的核心原因。


总结

  • T extends any 在联合类型时触发 分布式条件类型
  • TypeScript 会将联合类型拆分为单个成员,分别应用条件类型逻辑。
  • UnionToIntersection 中,这一特性用于对联合类型的每个成员生成函数类型 (x: T) => any,然后再利用交叉类型将它们合并。