编程TypeScript 架构师

TypeScript 中的条件类型是如何工作的?什么是条件类型的分发行为以及如何处理它?请举例说明。

用 Hintsage AI 助手通过面试

答案。

TypeScript 中的条件类型允许通过 T extends U ? X : Y 的原则通过另一个类型来描述一个类型。这些类型在构建复杂的类型逻辑时,尤其是在库和 API 声明中,提供了强大的功能。

基本条件类型示例:

type IsString<T> = T extends string ? 'yes' : 'no'; type A = IsString<string>; // 'yes' type B = IsString<number>; // 'no'

分发行为

如果条件类型应用于联合类型,则 TypeScript 会对联合中的每个元素分别"分发"条件。这被称为条件类型的分发行为。

示例:

type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean

这是一个非常强大的特性,但在处理数组和类型映射时,结果可能会令人困惑。

如何避免分发行为

将类型包裹在元组中:

type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean

有陷阱的问题。

问题: "如果不将条件类型与联合类型包裹在元组中,会发生什么?结果是否总是与普通逻辑相同?"

答案: 结果可能是意想不到的!由于分发性,条件会分别应用于每个联合类型的成员。要严格比较整个联合类型,需要使用包装(元组)。

示例:

type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean,而不仅仅是 boolean

由于未知主题细节而导致的实际错误示例。


故事

在一个序列化库中,使用条件类型检查数据结构,但忘记将泛型参数包裹在元组中。结果在复杂的联合类型声明中出现问题,编译器在使用 API 时生成了不可预测的类型。


故事

在尝试实现用于处理模型字段的类型转换时,部分信息丢失:由于分发性导致的联合,遗漏了几个逻辑分支,最终类型变得过于宽松。


故事

开发者认为 T extends SomeType 在条件类型内部的表现将如我们预期的那样适用于整个对象,但发生了"分散"——编译器指出不一致性,从而引发了类型混乱和生成自动文档时的严重错误。