深入浅出 TS 条件类型的 infer 类型推断 🤨🤨🤨

TypeScript 中的 infer 关键字用于条件类型中,用于推断一个类型。这在处理复杂类型时非常有用,可以帮助我们提取或转换类型。

基本用法

infer 关键字只能在条件类型中使用,通常与泛型和 extends 关键字一起使用。它的语法如下:

ts 复制代码
type Moment<T> = T extends infer U ? U : never;

这里 T extends infer U 意味着我们试图推断类型 T 并将其赋值给 U。如果类型推断成功,那么 U 就是我们推断出的类型。

接下来我们可以用它来推断不同的类型,这里有几个例子:

ts 复制代码
type Moment<T> = T extends infer U ? U : never;

type StringType = Moment<string>; // string
type NumberType = Moment<number>; // number
type UnionType = Moment<string | number>; // string | number

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

type UserType = Moment<User>; // User

在这些例子中,Moment<T> 实际上只是返回了 T 的类型,不进行任何转换或处理。这主要是用来演示条件类型和类型推断的基本用法。

常见示例

提取函数返回类型;

假设我们有一个函数类型,我们想要提取这个函数的返回类型,可以这样做:

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

type ExampleFunction = (x: number, y: string) => boolean;
type ReturnTypeOfExampleFunction = GetReturnType<ExampleFunction>; // boolean

在上面的这些代码中:

  1. T extends (...args: any[]) => infer R: 这部分检查类型 T 是否是一个函数类型。(...args: any[]) 表示该函数可以接受任意数量的参数。
  2. infer R: 如果 T 是一个函数类型,那么 infer R 将推断出函数的返回值类型,并将其赋给类型变量 R。
  3. ? R : never: 如果 T 是一个函数类型,则返回推断出的返回值类型 R;否则返回 never 类型。

提取数组元素类型

我们还可以使用 infer 来提取数组元素的类型:

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

type Moment = string[];
type Example1Array = Array<string>;

type ElementTypeOfExampleArray = GetArrayElementType<Moment>; // string
type ElementTypeOfExample1Array = GetArrayElementType<Example1Array>; //string

在这里,我们使用 T extends (infer U)[] 来推断数组元素的类型 U。其中 T 是 string[],U 是 string[] 中的是 string。

ts 复制代码
string[] extends (infer U)[ ]

值得注意的是,infer 声明仅在条件类型的 extends 子句中才允许使用,并且 infer 声明的类型变量只有在 true 分支中可用。

进阶示例

提取 Promise 的值类型

如果我们有一个 Promise 类型,我们可以提取其解析后的值类型:

ts 复制代码
type GetPromiseValueType<T> = T extends Promise<infer U> ? U : never;

// 示例
type ExamplePromise = Promise<number>;
type ValueTypeOfExamplePromise = GetPromiseValueType<ExamplePromise>; // number

在上面的这些代码中:

  1. T extends Promise<infer U>:这部分检查类型 T 是否是一个 Promise 类型。

  2. infer U:如果 T 是一个 Promise 类型,那么 infer U 将推断出 Promise 的值的类型,并将其赋给类型变量 U。

  3. ? U : never:如果 T 是一个 Promise 类型,则返回推断出的值的类型 U;否则返回 never 类型。

提取函数参数类型

有时我们需要获取一个函数的参数类型。可以使用 infer 来实现:

ts 复制代码
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

type ExampleFunction = (a: number, b: string) => void;
type Params = GetParameters<ExampleFunction>; // [number, string]

在上面的这些代码中:

  1. T extends (...args: infer P) => any:这部分检查类型 T 是否是一个函数类型。

  2. infer P:如果 T 是一个函数类型,那么 infer P 将推断出函数的参数类型,并将其赋给类型变量 P。

  3. ? P : never:如果 T 是一个函数类型,则返回推断出的参数类型 P;否则返回 never 类型。

提取构造函数参数类型

我们还可以使用 infer 来提取类构造函数的参数类型:

ts 复制代码
type ConstructorParameters<T> = T extends new (...args: infer P) => any
  ? P
  : never;

class ExampleClass {
  constructor(public a: number, public b: string) {}
}

type Params = ConstructorParameters<typeof ExampleClass>; // [number, string]

条件类型中的复杂推断

假设我们需要在条件类型中使用复杂的推断逻辑:

ts 复制代码
type IsArray<T> = T extends (infer U)[] ? U : never;
type IsFunction<T> = T extends (...args: any[]) => infer R ? R : never;

type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;

// 示例
type ArrayType = ExtractType<string[]>; // string
type FunctionReturnType = ExtractType<() => number>; // number
type DefaultType = ExtractType<boolean>; // boolean

在这段代码中,如下:

ts 复制代码
type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;
  1. T extends any[] ? IsArray<T> :如果 T 是一个数组类型,则返回 IsArray,即数组元素的类型。

  2. T extends (...args: any[]) => any ? IsFunction<T>: 如果 T 是一个函数类型,则返回 IsFunction,即函数的返回值类型。

  3. T:如果 T 既不是数组类型也不是函数类型,则返回 T 本身。

总结

infer 关键字用于条件类型中,以从其他类型中推断出一个新的类型变量。它允许在类型检查时提取和使用特定的子类型或属性,从而增强类型系统的表达能力和灵活性。简单来说,infer 可以帮助从复杂类型中自动提取出所需的部分类型。

TypeScript 中的 infer 关键字用于条件类型中,用于推断一个类型。这在处理复杂类型时非常有用,可以帮助我们提取或转换类型。

基本用法

infer 关键字只能在条件类型中使用,通常与泛型和 extends 关键字一起使用。它的语法如下:

ts 复制代码
type Moment<T> = T extends infer U ? U : never;

这里 T extends infer U 意味着我们试图推断类型 T 并将其赋值给 U。如果类型推断成功,那么 U 就是我们推断出的类型。

接下来我们可以用它来推断不同的类型,这里有几个例子:

ts 复制代码
type Moment<T> = T extends infer U ? U : never;

type StringType = Moment<string>; // string
type NumberType = Moment<number>; // number
type UnionType = Moment<string | number>; // string | number

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

type UserType = Moment<User>; // User

在这些例子中,Moment<T> 实际上只是返回了 T 的类型,不进行任何转换或处理。这主要是用来演示条件类型和类型推断的基本用法。

常见示例

提取函数返回类型;

假设我们有一个函数类型,我们想要提取这个函数的返回类型,可以这样做:

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

type ExampleFunction = (x: number, y: string) => boolean;
type ReturnTypeOfExampleFunction = GetReturnType<ExampleFunction>; // boolean

在上面的这些代码中:

  1. T extends (...args: any[]) => infer R: 这部分检查类型 T 是否是一个函数类型。(...args: any[]) 表示该函数可以接受任意数量的参数。
  2. infer R: 如果 T 是一个函数类型,那么 infer R 将推断出函数的返回值类型,并将其赋给类型变量 R。
  3. ? R : never: 如果 T 是一个函数类型,则返回推断出的返回值类型 R;否则返回 never 类型。

提取数组元素类型

我们还可以使用 infer 来提取数组元素的类型:

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

type Moment = string[];
type Example1Array = Array<string>;

type ElementTypeOfExampleArray = GetArrayElementType<Moment>; // string
type ElementTypeOfExample1Array = GetArrayElementType<Example1Array>; //string

在这里,我们使用 T extends (infer U)[] 来推断数组元素的类型 U。其中 T 是 string[],U 是 string[] 中的是 string。

ts 复制代码
string[] extends (infer U)[ ]

值得注意的是,infer 声明仅在条件类型的 extends 子句中才允许使用,并且 infer 声明的类型变量只有在 true 分支中可用。

进阶示例

提取 Promise 的值类型

如果我们有一个 Promise 类型,我们可以提取其解析后的值类型:

ts 复制代码
type GetPromiseValueType<T> = T extends Promise<infer U> ? U : never;

// 示例
type ExamplePromise = Promise<number>;
type ValueTypeOfExamplePromise = GetPromiseValueType<ExamplePromise>; // number

在上面的这些代码中:

  1. T extends Promise<infer U>:这部分检查类型 T 是否是一个 Promise 类型。

  2. infer U:如果 T 是一个 Promise 类型,那么 infer U 将推断出 Promise 的值的类型,并将其赋给类型变量 U。

  3. ? U : never:如果 T 是一个 Promise 类型,则返回推断出的值的类型 U;否则返回 never 类型。

提取函数参数类型

有时我们需要获取一个函数的参数类型。可以使用 infer 来实现:

ts 复制代码
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

type ExampleFunction = (a: number, b: string) => void;
type Params = GetParameters<ExampleFunction>; // [number, string]

在上面的这些代码中:

  1. T extends (...args: infer P) => any:这部分检查类型 T 是否是一个函数类型。

  2. infer P:如果 T 是一个函数类型,那么 infer P 将推断出函数的参数类型,并将其赋给类型变量 P。

  3. ? P : never:如果 T 是一个函数类型,则返回推断出的参数类型 P;否则返回 never 类型。

提取构造函数参数类型

我们还可以使用 infer 来提取类构造函数的参数类型:

ts 复制代码
type ConstructorParameters<T> = T extends new (...args: infer P) => any
  ? P
  : never;

class ExampleClass {
  constructor(public a: number, public b: string) {}
}

type Params = ConstructorParameters<typeof ExampleClass>; // [number, string]

条件类型中的复杂推断

假设我们需要在条件类型中使用复杂的推断逻辑:

ts 复制代码
type IsArray<T> = T extends (infer U)[] ? U : never;
type IsFunction<T> = T extends (...args: any[]) => infer R ? R : never;

type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;

// 示例
type ArrayType = ExtractType<string[]>; // string
type FunctionReturnType = ExtractType<() => number>; // number
type DefaultType = ExtractType<boolean>; // boolean

在这段代码中,如下:

ts 复制代码
type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;
  1. T extends any[] ? IsArray<T> :如果 T 是一个数组类型,则返回 IsArray,即数组元素的类型。

  2. T extends (...args: any[]) => any ? IsFunction<T>: 如果 T 是一个函数类型,则返回 IsFunction,即函数的返回值类型。

  3. T:如果 T 既不是数组类型也不是函数类型,则返回 T 本身。

总结

infer 关键字用于条件类型中,以从其他类型中推断出一个新的类型变量。它允许在类型检查时提取和使用特定的子类型或属性,从而增强类型系统的表达能力和灵活性。简单来说,infer 可以帮助从复杂类型中自动提取出所需的部分类型。

相关推荐
光影少年12 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
As977_13 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
susu108301891115 分钟前
vue3 css的样式如果background没有,如何覆盖有background的样式
前端·css
Ocean☾17 分钟前
前端基础-html-注册界面
前端·算法·html
Rattenking17 分钟前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
Dragon Wu19 分钟前
前端 Canvas 绘画 总结
前端
CodeToGym24 分钟前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化
~甲壳虫25 分钟前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫29 分钟前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
Cwhat30 分钟前
前端性能优化2
前端