Skip to content

条件类型 (Conditional Types)

条件类型是 TypeScript 中一种强大的类型系统特性,它允许你根据某些条件来选择不同的类型。通过使用条件类型,你可以创建出更加灵活且类型安全的代码,适用于复杂的类型转换和工具函数设计。

1. 基本概念

条件类型的语法如下:

typescript
type ConditionalType<T> = T extends U ? X : Y;
  • T 是一个泛型参数。
  • U 是一个用于比较的类型。
  • XY 是基于条件的结果类型,即如果 T 扩展自 U,则结果为 X,否则为 Y

2. 示例解析

示例 1: 基本条件类型

typescript
type IsString<T> = T extends string ? true : false;

console.log(IsString<string>); // 输出: true
console.log(IsString<number>); // 输出: false

在这个例子中,IsString 根据传入的泛型参数是否为 string 类型返回 truefalse

示例 2: 结合泛型的条件类型

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

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

在这个例子中,UnwrapPromise 可以从 Promise 中提取出包裹的类型,如果传入的不是 Promise 类型,则直接返回原类型。

示例 3: 使用多个条件

typescript
type TypeCheck<T> = T extends string
  ? "It's a string"
  : T extends number
  ? "It's a number"
  : "Unknown type";

console.log(TypeCheck<string>); // 输出: "It's a string"
console.log(TypeCheck<number>); // 输出: "It's a number"
console.log(TypeCheck<boolean>); // 输出: "Unknown type"

在这个例子中,TypeCheck 根据传入的泛型参数选择不同的字符串字面量类型。

示例 4: 基础数据类型判断

typescript
type t1 = 1 extends number ? true : false; // true
type t2 = "1" extends number ? true : false; // false
type t3 = string extends object ? true : false; // false
type t4 = string extends Object ? true : false; // true
type t5 = { a: 1 } extends object ? true : false; // true
type t6 = { a: 1; b: 2 } extends { a: 1 } ? true : false; // true
type t7 = { a: 1 } extends { a: 1; b: 2 } ? true : false; // false
type t71 = 1 | 2 extends 1 | 2 | 3 ? true : false; // true
type t72 = 1 | 2 | 3 extends 1 | 2  ? true : false; // false
type t8 = string extends {} ? true : false; // true

type t9 = {} extends object ? true : false; // true
type t10 = object extends {} ? true : false; // true
type t11 = {} extends Object ? true : false; // true
type t12 = Object extends {} ? true : false; // true
type t13 = object extends Object ? true : false; // true
type t14 = Object extends object ? true : false; // true

// 原始类型的子面量类型 < 原始类型 < 原始类型对应的装箱类型 < Object
type t15 = string extends any ? true : false; // true
type t16 = Object extends any ? true : false; // true
type t17 = Object extends unknown ? true : false; // true

type t18 = any extends Object ? 1 : 2; // 1 | 2
type t19 = any extends "hello" ? 1 : 2; // 1 | 2

type t20 = unknown extends any ? true : false; // true
type t21 = any extends unknown ? true : false; // true

3. 内置工具类型中的条件类型

TypeScript 提供了一些常用的内置工具类型,这些工具类型内部使用了条件类型:

  • Exclude<T, U>:从 T 中排除所有可以赋值给 U 的类型。
  • Extract<T, U>:从 T 中提取出所有可以赋值给 U 的类型。
  • NonNullable<T>:从 T 中排除 nullundefined
  • ReturnType<T>:获取函数类型的返回值类型。
  • InstanceType<T>:获取构造函数类型的实例类型。

示例:使用内置工具类型

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

function add(a: number, b: number): number {
  return a + b;
}

type AddReturnType = ReturnType<typeof add>; // number

class MyClass {}

type MyClassInstance = InstanceType<typeof MyClass>; // MyClass

4. 高级用法

4.1 分配条件类型(Distributive Conditional Types)

当条件类型作用于联合类型时,TypeScript 会自动将联合类型分配到条件类型中,分别处理每个成员。

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 将数组类型转换为其元素类型,而对于非数组类型则保持不变。

4.2 推断类型(Type Inference in Conditional Types)

在条件类型中使用 infer 关键字可以从类型中推断出子类型。

typescript
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type FuncReturn1 = GetReturnType<() => string>; // string
type FuncReturn2 = GetReturnType<(a: number) => boolean>; // boolean

在这个例子中,GetReturnType 可以从函数签名中提取出返回值类型。

4.3 条件类型与映射类型的结合

你可以将条件类型与映射类型结合使用,以实现更复杂的类型转换逻辑。

typescript
type TransformProps<T> = {
  [P in keyof T]: T[P] extends string ? number : T[P];
};

interface Person {
  name: string;
  age: number;
}

type TransformedPerson = TransformProps<Person>; // { name: number; age: number; }

在这个例子中,TransformProps<Person>Person 类型中的所有 string 类型属性转换为 number 类型。

5. 注意事项

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

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

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

6. 总结

条件类型是 TypeScript 中一个非常重要的特性,它使得你可以根据条件动态地选择或转换类型。通过合理使用条件类型,你可以编写出更加灵活且类型安全的代码。

Released under the MIT License.