深入浅出 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 可以帮助从复杂类型中自动提取出所需的部分类型。

相关推荐
学不会•14 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS1 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年5 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder5 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript