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

TypeScript递归联合类型为何无法包含预期的函数子类型?

TypeScript递归联合类型无法匹配预期子类型的问题解析

你尝试用递归联合类型表达无限层级的函数类型系列:

type Q0 = number;
type Q1 = (x: number) => number;
type Q2 = (x: (x: number) => number) => number;
type Q3 = (x: (x: (x: number) => number) => number) => number;
// ...

你定义的递归联合类型:

type Q = number | ((x: Q) => number);

用辅助类型检查时发现不符合预期:

type IsSubtypeOf<S, T> = S extends T ? true : false;
type _1 = IsSubtypeOf<number, Q>; // => true
type _2 = IsSubtypeOf<(x: number) => number, Q>; // => false

而手动展开一层的定义却能正常匹配:

type Q2 = number | ((x: number) => number) | ((x: Q2) => number);
type _3 = IsSubtypeOf<(x: number) => number, Q2>; // => true

核心原因:递归联合的循环约束 vs 无限层级联合

你忽略了TypeScript递归类型的本质:Q = number | ((x: Q) => number)是循环自引用类型,而非你预期的“无限层级类型的联合”。

子类型检查的逻辑细节

当检查(x: number) => number extends Q时,TypeScript会把Q拆分成两个分支逐一匹配:

  1. 是否是number的子类型?显然不是,函数类型和原始类型无继承关系。
  2. 是否是(x: Q) => number的子类型?这里要注意函数参数的逆变特性:
    函数类型A extends B的前提是,B的参数类型是A参数类型的子类型(逆变)。也就是说,要让(x: number) => number extends (x: Q) => number,需要满足Q extends number——但Q是number | 函数类型的联合,显然Q并不完全属于number,这个匹配不成立。

两个分支都不通过,最终返回false。

而Q2的定义直接把(x: number) => number作为联合成员之一,检查时直接匹配到该分支,自然返回true。

正确表达无限层级类型的思路

TypeScript无法直接定义“无限数量类型的联合”,但可以通过递归条件类型来模拟层级构建:

// 辅助类型:实现数字减1(可扩展更多数字范围)
type Decrement<T extends number> = 
  T extends 0 ? never :
  T extends 1 ? 0 :
  T extends 2 ? 1 :
  T extends 3 ? 2 :
  T extends 4 ? 3 :
  never;

// 递归构建指定层级的类型
type QLevel<N extends number, Current = number> = 
  N extends 0 ? Current : QLevel<Decrement<N>, (x: Current) => number>;

// 用条件类型模拟所有层级的联合(实际使用中可通过泛型约束覆盖所需层级)
type Q = number | ((x: QLevel<infer N>) => number);

如果需要更灵活的子类型匹配,也可以调整递归逻辑,让类型检查时能正确识别层级关系。

内容的提问来源于stack exchange,提问作者William Lewis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:52:40