分布式条件类型 (Distributive Conditional Types)
分布式条件类型是 TypeScript 中一种特别的条件类型行为,它在处理联合类型时自动分配(distribute)条件类型到联合类型的每个成员。这种特性使得你可以对联合类型中的每个成员单独应用条件逻辑,从而实现更细粒度的类型转换。
1. 基本概念
当条件类型 T extends U ? X : Y 的 T 是一个联合类型时,TypeScript 会将这个联合类型拆分,并分别应用条件类型到每个成员。换句话说,对于联合类型 A | B | C,条件类型会被视为 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)。
2. 语法
type DistributiveConditionalType<T> = T extends U ? X : Y;T是一个泛型参数,通常是一个联合类型。U是一个用于比较的类型。X和Y是基于条件的结果类型。
3. 示例解析
示例 1: 分配条件类型
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 关键字
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: 复杂的联合类型
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 仅保留联合类型中的 number 和 string 类型,其他类型被转换为 never,最终结果为 number | string。
4. 内置工具类型中的分配条件类型
许多内置工具类型内部使用了分配条件类型:
Exclude<T, U>:从T中排除所有可以赋值给U的类型。Extract<T, U>:从T中提取出所有可以赋值给U的类型。NonNullable<T>:从T中排除null和undefined。
示例:使用内置工具类型
type OnlyStrings = Exclude<string | number | boolean, number | boolean>; // string
type JustNumbers = Extract<string | number | boolean, number>; // number
type NotNullish = NonNullable<string | null | undefined>; // string5. 注意事项
分配行为的控制:如果你不希望条件类型对联合类型进行分配,可以在条件类型的左侧使用括号来阻止分配行为。例如:
typescripttype NotDistributive<T> = [T] extends [any[]] ? T[number] : T; type Result = NotDistributive<string | string[]>; // string | string[]在这个例子中,
NotDistributive不会对联合类型string | string[]进行分配,因此结果仍然是string | string[]。复杂性管理:虽然分配条件类型非常强大,但在实际开发中应尽量保持类型定义的简洁性和可读性,避免过度复杂的嵌套条件。
性能考虑:复杂的条件类型可能会增加编译时间,对于大型项目应权衡其对编译时间和复杂度的影响。
类型安全:确保条件逻辑不会破坏类型的安全性,特别是在处理联合类型和交叉类型时。
6. 总结
分布式条件类型是 TypeScript 中一个非常重要的特性,它使得你可以对联合类型中的每个成员单独应用条件逻辑,从而实现更细粒度的类型转换。通过合理使用分配条件类型,你可以编写出更加灵活且类型安全的代码。