TypeScript函数中使用递归类型报深度超限错误如何解决?
问题1:你对报错原因的判断完全正确
TypeScript的递归类型校验触发时机分为两种场景:
- 传入具体类型时,TS可以直接遍历到类型的终止节点,只要实际嵌套深度不超过默认的50层限制就不会报错
- 传入未实例化的泛型时,TS无法预判泛型最终的实际嵌套深度,为了避免编译器陷入无限递归卡死,会在定义阶段就执行递归深度校验,一旦无法明确推导终止条件就会抛出你遇到的报错。
问题2:为什么TS不等待函数实例化再做校验
TypeScript的类型系统是设计为编译期静态校验的,泛型函数的合法性校验发生在函数定义阶段,要求函数逻辑对所有符合泛型约束的入参都合法,不可能将校验延迟到调用实例化阶段:
- 如果延迟到实例化阶段校验,会出现同一个函数定义,有的入参合法、有的入参触发类型错误的情况,不符合静态类型系统的设计原则
- 大量泛型函数延迟到调用时校验,会大幅提升编译期的性能开销,甚至出现大型项目编译卡死的问题
问题3:规避方案及最优解说明
你提到的通过计数限制递归深度确实是目前的最优解,没有其他更简单的方案可以绕开这个限制。
为什么你调整后的代码阈值超过10就报错
你印象中TS递归上限是50,这个数值是针对具体类型场景的限制,而泛型场景下的递归计数消耗要高很多:
你的递归类型每一层会做两次类型展开:
- 遍历
keyof TRecord生成映射类型,每个key都要做一次条件判断 - 取
[keyof TRecord]得到联合类型时,要对每个key的递归结果做合并推导
相当于每层实际消耗的递归计数是你预期的2~3倍,再加上泛型场景下TS的递归阈值会比具体类型场景更严格,所以阈值设为10的时候就会触发上限。
如果需要处理更深的嵌套,可以把终止阈值调整到20~25,完全可以覆盖绝大多数业务场景的对象嵌套深度,也不会触发递归报错。你也可以改用更清晰的递减计数写法:
type MaxDepth = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] type AppendToTuple<T extends any[], U> = [...T, U] type NestedRecord = Record<string, any> type RecursiveGetIndex< TRecord extends NestedRecord, TPreviousIndices extends any[] = [], TDepth extends number = 20 > = TDepth extends 0 ? TPreviousIndices : { [K in keyof TRecord]: TRecord[K] extends NestedRecord ? RecursiveGetIndex< TRecord[K], AppendToTuple<TPreviousIndices,K>, MaxDepth[TDepth] > : AppendToTuple<TPreviousIndices, K> }[keyof TRecord]
内容的提问来源于stack exchange,提问作者sam256
相关产品推荐
相关产品推荐

