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
相关产品推荐
相关产品推荐

