Skip to content

分布式条件类型 (Distributive Conditional Types)

分布式条件类型是 TypeScript 中一种特别的条件类型行为,它在处理联合类型时自动分配(distribute)条件类型到联合类型的每个成员。这种特性使得你可以对联合类型中的每个成员单独应用条件逻辑,从而实现更细粒度的类型转换。

1. 基本概念

当条件类型 T extends U ? X : YT 是一个联合类型时,TypeScript 会将这个联合类型拆分,并分别应用条件类型到每个成员。换句话说,对于联合类型 A | B | C,条件类型会被视为 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)

2. 语法

typescript
type DistributiveConditionalType<T> = T extends U ? X : Y;
  • T 是一个泛型参数,通常是一个联合类型。
  • U 是一个用于比较的类型。
  • XY 是基于条件的结果类型。

3. 示例解析

示例 1: 分配条件类型

typescript
type Flatten<T> = T extends any[] ? T[number] : T;

type Result1 = Flatten<string[]>; // string
type Result2 = Flatten<number>; // number
type Result3 = Flatten<string | string[]>; // string | string

在这个例子中,Flatten 将数组类型转换为其元素类型,而对于非数组类型则保持不变。对于联合类型 string | string[],条件类型被分配到每个成员,结果为 string | string

示例 2: 使用 infer 关键字

typescript
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type Result1 = UnwrapPromise<Promise<string>>; // string
type Result2 = UnwrapPromise<number>; // number
type Result3 = UnwrapPromise<Promise<string> | number>; // string | number

在这个例子中,UnwrapPromise 可以从 Promise 中提取出包裹的类型,如果传入的是联合类型,则条件类型被分配到每个成员,结果为 string | number

示例 3: 复杂的联合类型

typescript
type ExtractNumberOrString<T> = T extends number | string ? T : never;

type Result1 = ExtractNumberOrString<number>; // number
type Result2 = ExtractNumberOrString<string>; // string
type Result3 = ExtractNumberOrString<number | string | boolean>; // number | string

在这个例子中,ExtractNumberOrString 仅保留联合类型中的 numberstring 类型,其他类型被转换为 never,最终结果为 number | string

4. 内置工具类型中的分配条件类型

许多内置工具类型内部使用了分配条件类型:

  • Exclude<T, U>:从 T 中排除所有可以赋值给 U 的类型。
  • Extract<T, U>:从 T 中提取出所有可以赋值给 U 的类型。
  • NonNullable<T>:从 T 中排除 nullundefined

示例:使用内置工具类型

typescript
type OnlyStrings = Exclude<string | number | boolean, number | boolean>; // string

type JustNumbers = Extract<string | number | boolean, number>; // number

type NotNullish = NonNullable<string | null | undefined>; // string

5. 注意事项

  • 分配行为的控制:如果你不希望条件类型对联合类型进行分配,可以在条件类型的左侧使用括号来阻止分配行为。例如:

    typescript
    type NotDistributive<T> = [T] extends [any[]] ? T[number] : T;
    
    type Result = NotDistributive<string | string[]>; // string | string[]

    在这个例子中,NotDistributive 不会对联合类型 string | string[] 进行分配,因此结果仍然是 string | string[]

  • 复杂性管理:虽然分配条件类型非常强大,但在实际开发中应尽量保持类型定义的简洁性和可读性,避免过度复杂的嵌套条件。

  • 性能考虑:复杂的条件类型可能会增加编译时间,对于大型项目应权衡其对编译时间和复杂度的影响。

  • 类型安全:确保条件逻辑不会破坏类型的安全性,特别是在处理联合类型和交叉类型时。

6. 总结

分布式条件类型是 TypeScript 中一个非常重要的特性,它使得你可以对联合类型中的每个成员单独应用条件逻辑,从而实现更细粒度的类型转换。通过合理使用分配条件类型,你可以编写出更加灵活且类型安全的代码。

Released under the MIT License.