类型推断与infer关键字

TypeScript的类型系统是其最强大的特性之一,而类型推断和infer关键字则是这个系统中较为高级但极其有用的部分。本文将深入探讨TypeScript中的类型推断机制以及如何使用infer关键字来提取和操作复杂类型。

类型推断基础

TypeScript的类型推断是指编译器在没有显式类型注解的情况下自动确定变量或表达式类型的能力。这种能力使得TypeScript在保持类型安全的同时,减少了冗余的类型注解。

typescript 复制代码
let x = 3; // TypeScript推断x为number类型
const arr = [1, 2, 3]; // 推断为number[]

条件类型与类型推断

条件类型是TypeScript 2.8引入的强大特性,它允许我们根据条件选择不同的类型:

typescript 复制代码
type IsString<T> = T extends string ? true : false;

infer关键字详解

infer关键字是条件类型中的特殊操作符,它允许我们在条件类型的"真"分支中声明一个类型变量,用于"捕获"类型信息。

基本用法

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

function foo(): number {
  return 42;
}

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

提取数组元素类型

typescript 复制代码
type ElementType<T> = T extends (infer U)[] ? U : never;

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

提取Promise的解析类型

typescript 复制代码
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;

type PromiseString = Promise<string>;
type StringType = UnpackPromise<PromiseString>; // string

高级应用场景

递归类型解包

typescript 复制代码
type DeepUnpackPromise<T> = 
  T extends Promise<infer U> ? DeepUnpackPromise<U> : T;

type NestedPromise = Promise<Promise<Promise<number>>>;
type Num = DeepUnpackPromise<NestedPromise>; // number

函数参数类型提取

typescript 复制代码
type FirstParam<T> = 
  T extends (arg1: infer P, ...args: any[]) => any ? P : never;

function greet(name: string, age: number): void {}
type GreetFirstParam = FirstParam<typeof greet>; // string

联合类型分发

typescript 复制代码
type Box<T> = { value: T };
type Unbox<T> = T extends Box<infer U> ? U : never;

type StringOrNumberBox = Box<string> | Box<number>;
type StringOrNumber = Unbox<StringOrNumberBox>; // string | number

注意事项与限制

  1. infer只能在条件类型的extends子句中使用
  2. 同一类型变量不能在多个位置infer不同的类型
  3. 复杂的嵌套类型可能会导致性能问题

结语

infer关键字为TypeScript的类型系统带来了极大的灵活性,使得我们能够从现有类型中提取和转换出新的类型。掌握这一特性可以显著提升类型定义的表达能力和代码的可维护性。通过本文的示例,希望读者能够理解并开始在项目中应用这些高级类型技术。