You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript条件类型中参数推断为交叉而非联合的原因咨询

为什么TypeScript函数参数的infer会得到交叉类型而非联合类型?

这个问题的核心在于TypeScript类型系统中的逆变位置类型推断规则——你这里的infer I正好处于函数参数这个逆变位置,所以多个约束会被合并为交叉类型,而非你预期的联合类型。

让我们一步步拆解你的例子:

type RetType<T> = T extends (...args: (infer I)[]) => infer R ? [I, R] : any;
type X = (a: number, b: string) => void;
type Q = RetType<X>; // 结果是 [number & string, void]

1. 函数参数是逆变位置

函数类型的兼容性遵循逆变规则:如果函数A可以安全赋值给函数B,那么B的参数类型必须是A参数类型的子类型(换句话说,A的参数类型是B参数类型的超类型)。

在你的代码中,X((number, string) => void)要匹配(...args: (infer I)[]) => infer R这个模板。这里的(...args: I[])表示模板函数可以接受任意数量的I类型参数。为了让X符合这个模板的约束,X必须能接受任意I类型的值作为它的第一个、第二个……参数。

2. 交叉类型的推断逻辑

要满足上述约束,I必须同时符合两个条件:

  • 任何I类型的值都能赋值给number(即I extends number);
  • 任何I类型的值都能赋值给string(即I extends string);

只有同时满足这两个条件的类型才是合法的I,也就是number和string的交集——number & string(虽然这个类型实际是never,但TypeScript会按照规则推断出这个交叉类型)。

3. 如何得到你预期的联合类型?

如果你的目标是把所有参数类型合并为联合类型,应该直接推断整个参数元组,再取元组元素的联合类型:

type RetType<T> = T extends (...args: infer Args) => infer R ? [Args[number], R] : any;
type X = (a: number, b: string) => void;
type Q = RetType<X>; // 结果是 [number | string, void]

这里infer Args直接捕获了完整的参数元组[number, string],Args[number]会返回元组所有元素的联合类型,这正是你最初预期的结果。

补充:协变vs逆变的合并规则

TypeScript的类型推断规则里:

  • 协变位置(比如函数返回值、对象属性)的多个infer会合并为联合类型;
  • 逆变位置(比如函数参数)的多个infer会合并为交叉类型。

这是类型系统保证类型安全的关键设计。

内容的提问来源于stack exchange,提问作者David Karapetyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:49:49