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

发布于:2025-04-03 ⋅ 阅读:(25) ⋅ 点赞:(0)

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

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

1. 分布式条件类型

(1) 定义
  • 当条件类型作用于一个裸类型参数(Naked Type Parameter),并且该参数是一个联合类型时,TypeScript 会将条件类型“分布”到联合类型的每个成员上。
  • 这种行为称为分布式条件类型
(2) 示例
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) 示例
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:函数参数的分布式条件类型
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:数组元素的分布式条件类型
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) 提取函数参数类型

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

示例代码
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) 处理多态函数

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

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

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

在这里:

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

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

6. 总结

  • 分发逆变推断的核心作用
    • 结合分布式条件类型和逆变的行为,动态推断出参数或返回值的类型。
    • 支持灵活的类型操作,特别是在处理函数联合类型时。
  • 常见场景
    • 提取函数参数类型。
    • 处理多态函数。
  • 注意事项
    • 理解逆变对联合类型的影响(交集类型)。
    • 避免过度复杂化类型定义。

网站公告

今日签到

点亮在社区的每一天
去签到