TypeScript 函数交叉类型推导疑问:h、i、e类型结果为何不符合预期
TypeScript函数交叉类型推导逻辑解析
前置核心规则
- 交叉类型本质:交叉类型表示值必须同时满足所有成员类型的约束,只要存在符合要求的值,交叉类型就不会退化为
never。 - 函数位置变种规则:函数类型中,参数位置属于逆变位置,返回值位置属于协变位置,类型推导的合并逻辑会随位置变化。
- infer 推导实现规则:当前版本TypeScript中,从交叉类型的多个成员匹配推导时,逆变位置的infer候选值会按交叉成员顺序依次覆盖,而非自动合并。
对应疑问的具体解释
为什么
type g = 1 & 2是never,但type h = ((x: 1) => 0) & ((x: 2) => 0)不是never?- 字面量类型
1和2是不可兼容的基础原子类型,不存在任何值能同时既是1又是2,因此它们的交叉结果为never。 - 但两个函数类型的交叉要求是:这个函数必须同时满足“能接收参数
1并返回0”且“能接收参数2并返回0”。这类函数是真实存在的——比如参数类型为1 | 2的函数,完全可以同时满足两个约束,因此整个交叉类型是合法结构,不会退化为never。
- 字面量类型
为什么
type i = ((x: 1 & 2) => 0)中的参数x不是never?- 这里存在认知偏差:
1 & 2的计算结果本身就是never,因此x的标注类型就是never,不存在“不是never”的情况。 - 实际疑惑的来源应该是“为什么这个函数类型没有退化为
never”:函数类型的合法性仅取决于结构是否可存在,哪怕参数类型是never(代表这个函数永远无法被合法传入参数调用),这个函数结构本身也是合法的,不会因为参数是never就把整个函数类型变成never。 - 补充:当
never出现在函数参数位置时,由于不存在任何类型为never的值,这个参数的约束永远不会被触发,相当于函数可以接收任意类型的参数调用,但不会让函数类型本身变为never。
- 这里存在认知偏差:
为什么
type e = (((x: 1) => 0) & ((x: 2) => 0)) extends (x: infer L) => 0 ? L : never的结果是2,而非1或never?- 首先这个交叉的函数类型本身是合法结构,不是
never,因此extends判定为成立,不会走到返回never的分支。 - 匹配过程中,TS会把交叉的两个成员逐个和目标模式
(x: infer L) => 0做比对:- 匹配第一个成员
(x:1) => 0时,推导得到L的第一个候选值为1 - 匹配第二个成员
(x:2) => 0时,推导得到L的第二个候选值为2
- 匹配第一个成员
- 按照当前TS的infer实现逻辑,交叉类型多成员匹配时,逆变位置的infer候选值不会自动合并,而是按成员顺序依次覆盖,最后一个匹配成员的推导结果会覆盖之前的所有候选值,因此最终
L会保留最后一个候选值2。 - 注意:这个行为是TS的实现细节,不是理论类型系统的设计预期,后续版本可能会调整该推导逻辑。
- 首先这个交叉的函数类型本身是合法结构,不是
内容的提问来源于stack exchange,提问作者xianshenglu
相关产品推荐
相关产品推荐

