【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 被分发为 string 和 number。
  • 条件类型分别应用于 string 和 number,最终结果是 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 提取了函数参数的类型。
  • 对于单个函数类型,结果是参数类型本身(如 string 或 number)。
  • 对于联合类型 (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 表示函数可以接受同时兼容 string 和 number 的值(即 never)。

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

6. 总结

  • 分发逆变推断的核心作用 :
    • 结合分布式条件类型和逆变的行为,动态推断出参数或返回值的类型。
    • 支持灵活的类型操作,特别是在处理函数联合类型时。
  • 常见场景 :
    • 提取函数参数类型。
    • 处理多态函数。
  • 注意事项 :
    • 理解逆变对联合类型的影响(交集类型)。
    • 避免过度复杂化类型定义。
相关推荐
高晶4 小时前
2A3 单端甲类功放从零制作全攻略:电路设计、焊接调试到煲机维护一站式指南
前端·设计模式
deli0074 小时前
塔防游戏里怪物为什么绕远路?Canvas 实测 A* 寻路 20 波
前端
小万7424 小时前
WHO 心理自助指南深度解析:Step-by-Step 与 Doing What Matters in Times of Stress,用循证协议重构心理韧性
前端
代码猎人4 小时前
前端怎么优化用户体验
前端
deli0074 小时前
批量加水印还在用在线工具?30 张图 Canvas 本地铺满实测
前端
鸿蒙开发4 小时前
扫描 PDF 看得见却搜不到?用 Tesseract.js + PDF.js 生成可搜索 PDF
前端·react.js
小万7424 小时前
ECC(Everything Claude Code)深度解析:把编程 Agent 从“聊天写代码”拉进工程现场的 Harness 系统
前端
用户2145549310154 小时前
VeapAI 自然语言操作实战|第 02 篇:语义层——把系统讲给模型听的四个构件
前端·后端
xxwl5855 小时前
区域赛算法模版第八章---贪心(Greedy)
c++·笔记·学习·算法
定栓5 小时前
Webpack迁到Vite第七节(一):全家桶移除 — 配置文件与构建核心
前端·webpack·node.js