跳到主要内容

条件类型和infer

在 TypeScript 中,条件类型(Conditional Types) 是一种根据类型之间的关系动态选择或转换类型的高级特性。它的语法类似于 JavaScript 中的三元表达式(condition ? trueType : falseType),但作用于类型层面,而非值层面。

核心思想
“如果类型 A 满足某种条件,则返回类型 B,否则返回类型 C。”


🔧 基本语法

SomeType extends OtherType ? TrueType : FalseType
  • extends 在这里表示 “可赋值性”(assignability),即 SomeType 是否兼容 OtherType
  • 整个表达式在编译时求值,不产生任何运行时代码。

✅ 简单示例

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

type A = IsString<'hello'>; // true
type B = IsString<42>; // false
type C = IsString<string>; // true

🌟 实际应用场景

1. 泛型中的类型过滤(Extract / Exclude)

TypeScript 内置的 ExcludeExtract 就是条件类型的经典应用:

// 排除某些类型
type Exclude<T, U> = T extends U ? never : T;

type T0 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'

// 提取可赋值的类型
type Extract<T, U> = T extends U ? T : never;

type T1 = Extract<'a' | 'b' | 1 | 2, string>; // 'a' | 'b'

2. 处理函数参数或返回值

type ReturnType<T> = T extends (...args: any) => infer R ? R : never;

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

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

🔍 这里用到了 infer 关键字 —— 它允许你从条件中“推断”出一个类型变量(如 R)。

3. 扁平化嵌套类型(Flattening)

type Flatten<T> = T extends Array<infer Item> ? Item : T;

type T2 = Flatten<string[]>; // string
type T3 = Flatten<number>; // number(不是数组,保持原样)

4. 根据输入类型决定输出类型

type Process<T> = T extends string
? { value: T; type: 'string' }
: T extends number
? { value: T; type: 'number' }
: { value: T; type: 'other' };

type T4 = Process<'hello'>; // { value: 'hello'; type: 'string' }
type T5 = Process<42>; // { value: 42; type: 'number' }

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

当条件类型的左侧是一个裸类型参数(naked type parameter),且传入的是联合类型时,TypeScript 会自动对联合类型的每个成员分别应用条件,再合并结果。

type ToArray<T> = T extends any ? T[] : never;

type StrNum = ToArray<string | number>;
// 等价于:
// (string extends any ? string[] : never) |
// (number extends any ? number[] : never)
// 结果:string[] | number[]

💡 “裸类型参数”指 T 直接出现在 extends 左侧,而不是被包裹(如 [T]Promise<T> 等)。

如果你不想分布,可以加一层包装:

type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type T6 = ToArrayNonDist<string | number>; // (string | number)[]

✅ 为什么需要条件类型?

  • 实现高度复用的工具类型(如 PartialRequiredPick 等底层都依赖它)
  • 根据输入类型智能推导输出类型
  • 构建类型安全的 API,比如 ORM、状态管理库等

📌 总结

概念说明
语法T extends U ? X : Y
作用在类型系统中实现“if-else”逻辑
关键能力类型过滤、类型提取、类型映射、配合 infer 推断
典型用途工具类型、函数重载模拟、API 类型设计
注意联合类型会触发“分布式”行为

💡 一句话记住
条件类型 = 类型世界的三元运算符,让类型也能“做判断”。

它是 TypeScript 类型系统图灵完备的关键特性之一,也是构建高级类型工具的基石。

infer 关键字

infer 是 TypeScript 中用于条件类型中的类型推断的关键字。它允许你在泛型条件类型中声明一个待推断的类型变量。

infer 只能在 extends 条件类型的子句中使用,用于提取类型的一部分。

1. 提取函数返回类型

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

function foo() {
return 123;
}

type FooReturn = ReturnType<typeof foo>; // number

2. 提取函数参数类型

type Parameters<T> = T extends (...args: infer P) => any ? P : never;

function bar(x: string, y: number) {}
type BarParams = Parameters<typeof bar>; // [string, number]

3. 提取数组/元组元素类型

type ElementType<T> = T extends (infer U)[] ? U : never;

type NumArray = number[];
type Num = ElementType<NumArray>; // number

type StrArray = string[];
type Str = ElementType<StrArray>; // string

4. 提取 Promise 的解析类型

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

type PromiseNumber = Promise<number>;
type NumberType = UnwrapPromise<PromiseNumber>; // number

type NotPromise = string;
type StringType = UnwrapPromise<NotPromise>; // string

5. 递归解构类型

// 提取多层 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U> ? DeepUnwrapPromise<U> : T;

type DeepPromise = Promise<Promise<number>>;
type DeepNumber = DeepUnwrapPromise<DeepPromise>; // number

6. 提取构造函数实例类型

type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;

class MyClass {}
type MyInstance = InstanceType<typeof MyClass>; // MyClass

7. 联合类型中的推断

// 提取所有函数的返回类型
type AllReturnTypes<T> = T extends any ? (T extends (...args: any[]) => infer R ? R : never) : never;

type Functions = [() => string, () => number];
type Returns = AllReturnTypes<Functions>; // string | number

实际应用示例

1. 路由参数提取

type ExtractRouteParams<T> = T extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractRouteParams<`${Rest}`> : T extends `${string}:${infer Param}` ? Param : never;

type Route = '/user/:id/post/:postId';
type Params = ExtractRouteParams<Route>; // "id" | "postId"

2. 响应数据处理

type ApiResponse<T> = {
data: T;
status: number;
};

type ExtractApiData<T> = T extends ApiResponse<infer U> ? U : never;

type UserResponse = ApiResponse<{ name: string; age: number }>;
type UserData = ExtractApiData<UserResponse>; // { name: string; age: number }

3. 递归类型处理

// 将嵌套数组展平
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;

type NestedArray = [1, [2, [3, 4]], 5];
type FlatArray = Flatten<NestedArray>; // 1 | 2 | 3 | 4 | 5