【TS学习】(18)分发逆变推断

在 TypeScript 中,分发逆变推断(Distributive and Contravariant Inference) 是一个相对高级且复杂的概念,涉及到分布式条件类型和逆变(Contravariance)的结合。理解这个概念需要对以下三个核心知识点有清晰的认识:

  1. 分布式条件类型(Distributive Conditional Types)
  2. 逆变(Contravariance)
  3. infer 关键字

1. 分布式条件类型

(1) 定义
  • 当条件类型作用于一个裸类型参数(Naked Type Parameter),并且该参数是一个联合类型时,TypeScript 会将条件类型"分布"到联合类型的每个成员上。
  • 这种行为称为分布式条件类型
(2) 示例
typescript 复制代码
type IsString<T> = T extends string ? true : false;

type Result = IsString<string | number>; // true | false

在这里:

  • string | number 被分发为 stringnumber
  • 条件类型分别应用于 stringnumber,最终结果是 true | false

2. 逆变(Contravariance)

(1) 定义
  • 逆变是指在函数参数中,子类型关系的方向与返回值相反。
  • 如果类型 A 是类型 B 的子类型,则函数 (arg: B) => void(arg: A) => void 的子类型。
(2) 示例
typescript 复制代码
type Func<T> = (arg: T) => void;

const anyFunc: Func<any> = (arg: any) => console.log(arg);
const stringFunc: Func<string> = anyFunc; // 合法:逆变

在这里:

  • 函数参数是逆变的,因此 Func<any>Func<string> 的子类型。

3. 分发逆变推断

当分布式条件类型与逆变结合时,可能会出现一些复杂的行为。这种现象被称为分发逆变推断

示例 1:函数参数的分布式条件类型
typescript 复制代码
type ParametersFromFunction<T> = T extends (arg: infer P) => any ? P : never;

type Func1 = ParametersFromFunction<(arg: string) => void>; // string
type Func2 = ParametersFromFunction<(arg: number) => void>; // number

type UnionFunc = ParametersFromFunction<((arg: string) => void) | ((arg: number) => void)>; // string & number

在这里:

  • ParametersFromFunction 使用 infer P 提取了函数参数的类型。
  • 对于单个函数类型,结果是参数类型本身(如 stringnumber)。
  • 对于联合类型 (arg: string) => void | (arg: number) => void,参数类型被推断为 string & number

这是因为:

  • 函数参数是逆变的,联合类型的逆变会导致参数类型变为所有可能类型的交集(string & number)。

示例 2:数组元素的分布式条件类型
typescript 复制代码
type ArrayElement<T> = T extends (infer E)[] ? E : never;

type Numbers = ArrayElement<number[]>; // number
type Strings = ArrayElement<string[]>; // string

type MixedArray = ArrayElement<(string | number)[]>; // string | number

在这里:

  • 数组元素是协变的,因此联合类型的分发结果是 string | number

4. 实际应用场景

(1) 提取函数参数类型

你可以使用分布式条件类型和逆变推断提取函数参数的类型。

示例代码
typescript 复制代码
type ParametersFromFunction<T> = T extends (arg: infer P) => any ? P : never;

type FuncUnion = ParametersFromFunction<((arg: string) => void) | ((arg: number) => void)>; // string & number

在这里:

  • 函数参数的逆变导致联合类型的推断结果是交集类型(string & number)。

(2) 处理多态函数

你可以使用分发逆变推断来处理多态函数的参数类型。

示例代码
typescript 复制代码
type Func<T> = (arg: T) => void;

type InferParameters<T> = T extends Func<infer P> ? P : never;

type Result = InferParameters<Func<string> | Func<number>>; // string & number

在这里:

  • InferParameters 使用 infer P 提取了函数参数的类型。
  • 因为函数参数是逆变的,联合类型的推断结果是交集类型(string & number)。

5. 注意事项

(1) 交集类型的意义
  • 在逆变推断中,联合类型的参数会被推断为交集类型。
  • 这是因为逆变要求参数类型必须兼容所有可能的情况。
示例
typescript 复制代码
type FuncUnion = ((arg: string) => void) | ((arg: number) => void);

type Parameters = ParametersFromFunction<FuncUnion>; // string & number

在这里:

  • 参数类型 string & number 表示函数可以接受同时兼容 stringnumber 的值(即 never)。

(2) 避免过度复杂
  • 分发逆变推断的结果可能会导致交集类型变得难以理解。
  • 在实际开发中,尽量避免过于复杂的类型操作。

6. 总结

  • 分发逆变推断的核心作用
    • 结合分布式条件类型和逆变的行为,动态推断出参数或返回值的类型。
    • 支持灵活的类型操作,特别是在处理函数联合类型时。
  • 常见场景
    • 提取函数参数类型。
    • 处理多态函数。
  • 注意事项
    • 理解逆变对联合类型的影响(交集类型)。
    • 避免过度复杂化类型定义。
相关推荐
余人于RenYu6 分钟前
前端插件使用汇总
前端·javascript
2301_7891695419 分钟前
前端对接下载文件接口、对接dart app
前端
A旧城以西24 分钟前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea
无所谓จุ๊บ25 分钟前
VTK知识学习(50)- 交互与Widget(一)
学习·vtk
邴越29 分钟前
OpenAI Function Calling 函数调用能力与外部交互
开发语言·前端·javascript
FAREWELL0007531 分钟前
C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法
学习·c#·面向对象·运算符重载·oop·拓展方法
uhakadotcom35 分钟前
React 和 Next.js 的基础知识对比
前端·面试·github
Billy Qin42 分钟前
Tree - Shaking
前端·javascript·vue.js
Theodore_102243 分钟前
ES6(8) Fetch API 详解
开发语言·前端·javascript·ecmascript·es6
月明长歌1 小时前
Vue + Axios + Mock.js 全链路实操:从封装到数据模拟的深度解析
前端·javascript·vue.js·elementui·es6