Skip to content

infer 操作符

概述

infer 操作符是 TypeScript 条件类型中最强大的特性之一,它允许我们在条件类型中推断(infer)类型,而不是显式地声明类型。infer 操作符只能在条件类型的 extends 子句中使用,用于从复杂类型中提取和推断出我们需要的类型信息。通过 infer,我们可以实现类型级别的"模式匹配",从函数类型、数组类型、Promise 类型等复杂类型中提取出我们需要的部分。

为什么需要 infer 操作符

在没有 infer 操作符的情况下,我们无法从复杂类型中提取类型信息:

typescript
// 没有 infer:无法从函数类型中提取返回类型
type GetReturnType<T> = T extends (...args: any[]) => any ? any : never;
// 问题:只能返回 any,无法获取实际的返回类型

// 使用 infer:可以从函数类型中提取返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 优势:可以精确提取返回类型 R

infer 操作符让我们能够:

  1. 提取函数返回类型:从函数类型中推断出返回类型
  2. 提取函数参数类型:从函数类型中推断出参数类型
  3. 提取数组元素类型:从数组类型中推断出元素类型
  4. 提取 Promise 类型:从 Promise 类型中推断出解析类型
  5. 类型级模式匹配:在类型层面实现复杂的类型提取和转换

基本语法

infer 的基本形式

infer 操作符的基本语法是在条件类型中使用 infer 关键字声明一个类型变量:

typescript
// 基本语法:T extends U ? infer X : Y
// 在条件类型的 true 分支中,X 是被推断出的类型

// 示例:推断函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

infer 的工作原理

infer 操作符的工作原理类似于类型级别的"模式匹配":

typescript
// 当 T 是函数类型时,infer R 会推断出函数的返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用示例
type Func1 = () => string;
type Return1 = GetReturnType<Func1>;  // 类型:string

type Func2 = (x: number) => boolean;
type Return2 = GetReturnType<Func2>;  // 类型:boolean

// 如果 T 不是函数类型,条件不满足,返回 never
type NotFunc = string;
type Return3 = GetReturnType<NotFunc>;  // 类型:never

提示

infer 操作符只能在条件类型的 extends 子句中使用,并且只能在条件类型的 true 分支(? 之后)使用,不能在 false 分支(: 之后)使用。

推断函数返回类型

基本用法

最常用的场景是从函数类型中提取返回类型:

typescript
// 推断函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用示例
function greet(name: string): string {
  return `Hello, ${name}!`;
}

type GreetReturn = GetReturnType<typeof greet>;
// 类型:string

// 处理异步函数
async function fetchUser(id: number): Promise<{ id: number; name: string }> {
  return { id, name: 'John' };
}

type FetchUserReturn = GetReturnType<typeof fetchUser>;
// 类型:Promise<{ id: number; name: string }>

处理泛型函数

infer 也可以处理泛型函数的返回类型:

typescript
// 推断泛型函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 泛型函数
function identity<T>(value: T): T {
  return value;
}

type IdentityReturn = GetReturnType<typeof identity>;
// 类型:<T>(value: T) => T 的返回类型,即 T

// 实际使用时
type Result = GetReturnType<typeof identity<string>>;
// 类型:string

处理函数重载

对于函数重载,infer 会推断出最后一个重载签名的返回类型:

typescript
// 函数重载
function process(value: string): string;
function process(value: number): number;
function process(value: string | number): string | number {
  return value;
}

type ProcessReturn = GetReturnType<typeof process>;
// 类型:string | number(最后一个重载签名的返回类型)

推断函数参数类型

推断所有参数类型

从函数类型中提取所有参数的类型:

typescript
// 推断函数参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

// 使用示例
function createUser(name: string, age: number, email: string): void {
  // 实现省略
}

type CreateUserParams = GetParameters<typeof createUser>;
// 类型:[name: string, age: number, email: string]

推断第一个参数类型

提取函数的第一个参数类型:

typescript
// 推断第一个参数类型
type GetFirstParameter<T> = T extends (first: infer F, ...args: any[]) => any 
  ? F 
  : never;

// 使用示例
function greet(name: string, greeting?: string): void {
  console.log(`${greeting || 'Hello'}, ${name}!`);
}

type GreetFirstParam = GetFirstParameter<typeof greet>;
// 类型:string

推断剩余参数类型

提取除第一个参数外的所有参数类型:

typescript
// 推断剩余参数类型
type GetRestParameters<T> = T extends (first: any, ...rest: infer R) => any 
  ? R 
  : never;

// 使用示例
function process(name: string, age: number, email: string): void {
  // 实现省略
}

type ProcessRestParams = GetRestParameters<typeof process>;
// 类型:[age: number, email: string]

推断数组和元组类型

推断数组元素类型

从数组类型中提取元素类型:

typescript
// 推断数组元素类型
type GetArrayElement<T> = T extends (infer E)[] ? E : never;

// 使用示例
type StringArray = string[];
type StringElement = GetArrayElement<StringArray>;
// 类型:string

type NumberArray = number[];
type NumberElement = GetArrayElement<NumberArray>;
// 类型:number

// 处理元组
type Tuple = [string, number, boolean];
type TupleElement = GetArrayElement<Tuple>;
// 类型:string | number | boolean(元组的所有元素类型的联合)

推断元组的第一个元素

从元组类型中提取第一个元素类型:

typescript
// 推断元组的第一个元素类型
type GetFirstElement<T extends readonly any[]> = T extends [infer F, ...any[]] 
  ? F 
  : never;

// 使用示例
type Tuple1 = [string, number, boolean];
type First1 = GetFirstElement<Tuple1>;
// 类型:string

type Tuple2 = [number];
type First2 = GetFirstElement<Tuple2>;
// 类型:number

type EmptyTuple = [];
type First3 = GetFirstElement<EmptyTuple>;
// 类型:never(空元组没有第一个元素)

推断元组的最后一个元素

从元组类型中提取最后一个元素类型:

typescript
// 推断元组的最后一个元素类型
type GetLastElement<T extends readonly any[]> = T extends [...any[], infer L] 
  ? L 
  : never;

// 使用示例
type Tuple1 = [string, number, boolean];
type Last1 = GetLastElement<Tuple1>;
// 类型:boolean

type Tuple2 = [string];
type Last2 = GetLastElement<Tuple2>;
// 类型:string

推断元组的剩余元素

提取除第一个元素外的所有元素:

typescript
// 推断元组的剩余元素
type GetTail<T extends readonly any[]> = T extends [any, ...infer Rest] 
  ? Rest 
  : never;

// 使用示例
type Tuple1 = [string, number, boolean];
type Tail1 = GetTail<Tuple1>;
// 类型:[number, boolean]

type Tuple2 = [string, number];
type Tail2 = GetTail<Tuple2>;
// 类型:[number]

type Tuple3 = [string];
type Tail3 = GetTail<Tuple3>;
// 类型:[]

推断 Promise 类型

基本用法

从 Promise 类型中提取解析类型:

typescript
// 推断 Promise 的解析类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// 使用示例
type PromiseString = Promise<string>;
type Unwrapped = UnwrapPromise<PromiseString>;
// 类型:string

type PromiseNumber = Promise<number>;
type Unwrapped2 = UnwrapPromise<PromiseNumber>;
// 类型:number

// 非 Promise 类型保持不变
type NotPromise = string;
type Unwrapped3 = UnwrapPromise<NotPromise>;
// 类型:string

深度解包嵌套 Promise

递归解包嵌套的 Promise 类型:

typescript
// 深度解包嵌套的 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U>
  ? DeepUnwrapPromise<U>
  : T;

// 使用示例
type NestedPromise = Promise<Promise<Promise<string>>>;
type DeepUnwrapped = DeepUnwrapPromise<NestedPromise>;
// 类型:string

type DoublePromise = Promise<Promise<number>>;
type DeepUnwrapped2 = DeepUnwrapPromise<DoublePromise>;
// 类型:number

多个 infer 推断

同时推断多个类型

在同一个条件类型中可以使用多个 infer

typescript
// 同时推断多个类型
type GetFirstAndRest<T extends readonly any[]> = T extends [infer First, ...infer Rest]
  ? { first: First; rest: Rest }
  : never;

// 使用示例
type Tuple = [string, number, boolean];
type Result = GetFirstAndRest<Tuple>;
// 类型:{ first: string; rest: [number, boolean] }

type Tuple2 = [string];
type Result2 = GetFirstAndRest<Tuple2>;
// 类型:{ first: string; rest: [] }

推断函数参数和返回类型

同时提取函数的参数类型和返回类型:

typescript
// 同时推断函数参数和返回类型
type GetFunctionInfo<T> = T extends (...args: infer P) => infer R
  ? { parameters: P; returnType: R }
  : never;

// 使用示例
function process(name: string, age: number): boolean {
  return age > 18;
}

type ProcessInfo = GetFunctionInfo<typeof process>;
// 类型:{ parameters: [name: string, age: number]; returnType: boolean }

推断数组的首尾元素

从数组中提取第一个和最后一个元素:

typescript
// 推断数组的首尾元素
type GetFirstAndLast<T extends readonly any[]> = T extends readonly [infer First, ...any[], infer Last]
  ? { first: First; last: Last }
  : never;

// 使用示例
type Tuple1 = [string, number, boolean];
type Result1 = GetFirstAndLast<Tuple1>;
// 类型:{ first: string; last: boolean }

type Tuple2 = [string, number, boolean, number];
type Result2 = GetFirstAndLast<Tuple2>;
// 类型:{ first: string; last: number }

实际应用场景

场景 1:实现内置工具类型

使用 infer 实现 TypeScript 内置的工具类型:

typescript
// 实现 ReturnType
type MyReturnType<T extends (...args: any) => any> = 
  T extends (...args: any) => infer R ? R : any;

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

// 实现 ConstructorParameters
type MyConstructorParameters<T extends new (...args: any) => any> = 
  T extends new (...args: infer P) => any ? P : never;

// 实现 InstanceType
type MyInstanceType<T extends new (...args: any) => any> = 
  T extends new (...args: any) => infer R ? R : any;

// 使用示例
class User {
  constructor(public name: string, public age: number) {}
}

type UserParams = MyConstructorParameters<typeof User>;
// 类型:[name: string, age: number]

type UserInstance = MyInstanceType<typeof User>;
// 类型:User

场景 2:API 响应类型处理

从 API 函数中提取响应类型:

typescript
// API 函数定义
async function fetchUser(id: number): Promise<{
  id: number;
  name: string;
  email: string;
}> {
  // 实现省略
  return { id, name: 'John', email: 'john@example.com' };
}

// 提取 API 响应类型
type ApiResponse<T> = T extends (...args: any[]) => Promise<infer R> ? R : never;

type UserResponse = ApiResponse<typeof fetchUser>;
// 类型:{ id: number; name: string; email: string }

// 创建类型安全的 API 调用函数
async function callApi<T extends (...args: any[]) => Promise<any>>(
  apiFn: T,
  ...args: Parameters<T>
): Promise<ApiResponse<T>> {
  return apiFn(...args);
}

// 使用示例
const user = await callApi(fetchUser, 1);
// user 类型:{ id: number; name: string; email: string }

场景 3:事件处理器类型提取

从事件处理器映射中提取事件类型:

typescript
// 定义事件处理器类型
type EventHandlers = {
  click: (event: MouseEvent) => void;
  keydown: (event: KeyboardEvent) => void;
  resize: (event: UIEvent) => void;
};

// 提取事件类型
type ExtractEventType<T> = T extends (event: infer E) => any ? E : never;

// 提取特定事件的事件类型
type ClickEvent = ExtractEventType<EventHandlers['click']>;
// 类型:MouseEvent

type KeydownEvent = ExtractEventType<EventHandlers['keydown']>;
// 类型:KeyboardEvent

// 创建类型安全的事件监听器
function on<T extends keyof EventHandlers>(
  event: T,
  handler: (event: ExtractEventType<EventHandlers[T]>) => void
): void {
  // 实现省略
}

// 使用示例
on('click', (event) => {
  // event 类型:MouseEvent
  console.log(event.clientX, event.clientY);
});

on('keydown', (event) => {
  // event 类型:KeyboardEvent
  console.log(event.key, event.code);
});

场景 4:深度提取嵌套类型

从复杂的嵌套类型中提取需要的类型:

typescript
// 复杂的嵌套类型
type ComplexType = {
  data: {
    users: Array<{
      id: number;
      profile: {
        name: string;
        avatar: Promise<string>;
      };
    }>;
  };
};

// 提取用户数组类型
type ExtractUsers<T> = T extends { data: { users: infer U } } ? U : never;

type Users = ExtractUsers<ComplexType>;
// 类型:Array<{ id: number; profile: { name: string; avatar: Promise<string> } }>

// 提取用户元素类型
type UserElement = GetArrayElement<Users>;
// 类型:{ id: number; profile: { name: string; avatar: Promise<string> } }

// 提取头像类型并解包 Promise
type AvatarType = UserElement['profile']['avatar'];
type UnwrappedAvatar = UnwrapPromise<AvatarType>;
// 类型:string

场景 5:函数组合类型推断

从函数组合中推断类型:

typescript
// 函数组合类型
type Compose<F extends (...args: any) => any, G extends (...args: any) => any> = 
  G extends (x: infer X) => any
    ? F extends (...args: infer Args) => X
      ? (...args: Args) => ReturnType<G>
      : never
    : never;

// 使用示例
function double(x: number): number {
  return x * 2;
}

function toString(x: number): string {
  return x.toString();
}

type Composed = Compose<typeof double, typeof toString>;
// 类型:(x: number) => string

// 实际使用
const composed: Composed = (x: number) => toString(double(x));
const result = composed(5);
// result 类型:string,值为 "10"

高级用法

条件推断

结合条件类型实现更复杂的类型推断:

typescript
// 如果类型是数组,提取元素类型;如果是函数,提取返回类型
type ExtractType<T> = 
  T extends (infer E)[]
    ? E
    : T extends (...args: any[]) => infer R
    ? R
    : T;

// 使用示例
type ArrayType = ExtractType<string[]>;
// 类型:string

type FunctionType = ExtractType<() => number>;
// 类型:number

type OtherType = ExtractType<string>;
// 类型:string

递归类型推断

使用 infer 实现递归类型推断:

typescript
// 递归提取所有 Promise 类型
type DeepUnwrapPromise<T> = T extends Promise<infer U>
  ? DeepUnwrapPromise<U>
  : T;

// 递归提取数组的深层元素类型
type DeepArrayElement<T> = T extends (infer E)[]
  ? E extends any[]
    ? DeepArrayElement<E>
    : E
  : T;

// 使用示例
type NestedArray = string[][][];
type DeepElement = DeepArrayElement<NestedArray>;
// 类型:string

类型约束推断

infer 中使用类型约束:

typescript
// 推断满足特定条件的类型
type ExtractStringKeys<T> = T extends { [K in infer Key]: infer Value }
  ? Key extends string
    ? Value extends string
      ? Key
      : never
    : never
  : never;

// 更实用的例子:提取函数类型中特定位置的参数
type ExtractNthParameter<
  T extends (...args: any[]) => any,
  N extends number
> = T extends (...args: infer P) => any
  ? P[N] extends undefined
    ? never
    : P[N]
  : never;

// 使用示例
function example(a: string, b: number, c: boolean): void {}
type SecondParam = ExtractNthParameter<typeof example, 1>;
// 类型:number

类型检查示例

常见错误

typescript
// ❌ 错误:infer 不能在条件类型的 false 分支使用
// type Wrong<T> = T extends string ? string : infer U;  // 语法错误

// ❌ 错误:infer 不能在 extends 子句外使用
// type Wrong<T> = infer U;  // 语法错误

// ❌ 错误:infer 变量名冲突
// type Wrong<T> = T extends (x: infer T) => any ? T : never;  // T 冲突

// ❌ 错误:无法推断时返回 never
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Wrong = GetReturnType<string>;  // 类型:never(因为 string 不是函数类型)

正确写法

typescript
// ✅ 正确:基本 infer 用法
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// ✅ 正确:多个 infer
type GetFirstAndRest<T> = T extends [infer First, ...infer Rest]
  ? { first: First; rest: Rest }
  : never;

// ✅ 正确:递归 infer
type DeepUnwrapPromise<T> = T extends Promise<infer U>
  ? DeepUnwrapPromise<U>
  : T;

// ✅ 正确:条件推断
type ExtractType<T> = 
  T extends (infer E)[] ? E :
  T extends (...args: any[]) => infer R ? R :
  T;

注意事项

提示

  • infer 操作符只能在条件类型的 extends 子句中使用
  • infer 只能在条件类型的 true 分支(? 之后)使用,不能在 false 分支使用
  • 可以使用多个 infer 同时推断多个类型
  • infer 变量名应该避免与外部类型参数冲突
  • infer 是类型级别的操作,不会影响运行时性能

注意

  • 当条件类型不满足时,infer 推断的类型会是 never
  • 对于函数重载,infer 会推断最后一个重载签名的类型
  • 对于联合类型,infer 的行为可能不符合预期,需要特别注意
  • 复杂的 infer 嵌套可能会影响类型检查性能
  • infer 推断是延迟的,只有在实际使用时才会计算

重要

  • infer 操作符是 TypeScript 类型级编程的核心特性之一
  • 理解 infer 对于掌握高级类型和工具类型至关重要
  • infer 与条件类型、映射类型结合使用,可以构建强大的类型系统
  • 许多 TypeScript 内置工具类型(如 ReturnTypeParameters)都是基于 infer 实现的

信息

infer 操作符的优势

  • 类型提取:从复杂类型中精确提取需要的类型
  • 类型推断:在类型层面实现模式匹配和类型推断
  • 类型工具构建:构建可复用的类型工具和类型操作
  • 类型安全:在编译时确保类型提取的正确性

infer 操作符的应用场景

  • 工具类型实现(ReturnType、Parameters、InstanceType 等)
  • API 响应类型提取
  • 事件处理器类型提取
  • 函数组合类型推断
  • 深度类型提取和转换
  • 框架和库的类型定义

相关链接

基于 VitePress 构建