TypeScript 条件类型:简单类型检查
TypeScript 的类型系统远不止于标注变量类型。条件类型(Conditional Types)是其最强大的特性之一,它允许你基于类型关系动态推导新类型,实现类型层面的逻辑分支,极大提升了类型表达的灵活性与精确性。
核心语法:T extends U ? X : Y
- 基础逻辑: 如果类型
T能赋值给类型U(即满足约束U),则此条件类型的结果是X,否则是Y。 - 类型层面的
if语句: 这本质上是类型空间的条件判断,在编译时进行计算。
为何强大?动态类型推导
条件类型的核心价值在于根据输入类型动态决定输出类型。例如,内置工具类型 Exclude<T, U> 的实现原理:
type Exclude<T, U> = T extends U ? never : T;
- 它遍历
T(联合类型) 中的每个成员。 - 如果某个成员能赋值给
U,则将其排除(结果为never)。 - 否则,保留该成员。
type Result = Exclude<'a' | 'b' | 'c', 'a' | 'b'>; // 'c'
关键特性:分布式条件类型
当条件类型作用于联合类型 (T 是联合类型如 A | B | C) 时,它会自动分发到联合类型的每个成员上:
type ToArray<T> = T extends any ? T[] : never;
type StrOrNumArray = ToArray<string | number>; // string[] | number[]
- 结果不是
(string | number)[],而是string[] | number[]。 - 这是处理联合类型时极其重要的行为模式。
infer 关键字:类型模式匹配
条件类型结合 infer 可以提取类型结构中的部分信息,如同解构:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
infer R声明了一个待推断的类型变量R。- 如果
T是函数类型,则匹配其返回类型并赋值给R,最终ReturnType<T>就是R。 - 否则,结果为
any。
实战应用示例:Flatten
type Flatten<T> = T extends Array<infer Item> ? Item : T;
type Nested = number[][];
type Flat = Flatten<Nested>; // number[]
- 检查
T是否是数组类型。 - 如果是,用
infer Item提取其元素类型Item,结果类型就是Item。 - 如果不是,直接返回
T本身。
最佳实践与注意点
- 优先内置工具: 许多实用条件类型(
Exclude,Extract,NonNullable,ReturnType,Parameters等)已内置在 TS 中,优先使用。 - 理解分发: 牢记联合类型在条件类型中的分发行为,这是核心也是常见困惑点。用
[T] extends [U]的形式可以禁用分发。 infer威力: 结合条件使用infer是构建复杂、灵活类型工具(如推导函数参数、Promise 解包等)的基石。- 避免过度复杂: 虽然强大,但过度嵌套的条件类型会降低代码可读性。保持适度,必要时添加注释。
浙公网安备 33010602011771号