递归类型性能优化:解决嵌套状态路径类型编译过慢问题
优化嵌套对象状态路径的递归类型性能
我用对象存储应用的全部状态,由于对象嵌套层级不固定,需要定义状态路径类型来选取部分状态。目前写的递归类型满足以下需求:
- 可描述从根节点到任意子属性的字符串数组路径
- 支持在任意层级终止路径
- 仅允许选取子节点,不能选兄弟或祖先节点
- 数组属性支持通过索引或断言函数选取元素
但哪怕是简单的状态接口,这个递归类型编译速度都极慢。我已经限制了类型深度,超过阈值后回退为any,但面对业务里的复杂状态接口,性能还是不行。希望优化这个类型,优先实现无限制递归,也接受手动循环10层级后回退为any的方案。
方案一:手动展开10层级(性能最优)
将递归逻辑拆分为静态定义的10个层级,彻底避免递归类型的性能开销,同时保留所有需求特性:
// 断言函数类型定义 export type PredicateFunction<ArrayType> = (item: ArrayType, index?: number) => boolean; export type IndexOrPredicate<Type> = number | PredicateFunction<Type>; // 顶层路径类型:允许空路径或进入第一层 export type StatePath<Obj> = [] | StatePathLevel1<Obj>; // 第1层处理逻辑 type StatePathLevel1<Obj> = Obj extends object ? (Obj extends readonly any[] ? // 数组:允许索引/断言,或继续进入第二层 [IndexOrPredicate<Obj[number]>] | StatePathLevel2<Obj[number], [IndexOrPredicate<Obj[number]>]> : // 对象:遍历所有合法键,允许选键后继续进入第二层 { [K in keyof Obj & string]: [K] | StatePathLevel2<Obj[K], [K]> }[keyof Obj & string] ) : []; // 第2层处理逻辑 type StatePathLevel2<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel3<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel3<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第3层处理逻辑 type StatePathLevel3<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel4<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel4<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第4层处理逻辑 type StatePathLevel4<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel5<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel5<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第5层处理逻辑 type StatePathLevel5<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel6<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel6<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第6层处理逻辑 type StatePathLevel6<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel7<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel7<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第7层处理逻辑 type StatePathLevel7<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel8<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel8<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第8层处理逻辑 type StatePathLevel8<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel9<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel9<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第9层处理逻辑 type StatePathLevel9<Obj, Prev extends any[]> = Obj extends object ? (Obj extends readonly any[] ? [...Prev, IndexOrPredicate<Obj[number]>] | StatePathLevel10<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : { [K in keyof Obj & string]: [...Prev, K] | StatePathLevel10<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : Prev; // 第10层及以后:回退为宽松类型,平衡性能与兼容性 type StatePathLevel10<Obj, Prev extends any[]> = Obj extends object ? [...Prev, ...(string | IndexOrPredicate<any>)[]] : Prev; // 示例状态接口 export interface State { app: { testEntry: string; testArray: { firstname: string, lastname: string, address?: { zip: number; street: string; doorNumbers: { number: number; appendixes: number[]; }[] } }[]; } } // 合法路径示例 const path1: StatePath<State> = ['app', 'testEntry']; const path2: StatePath<State> = ['app', 'testArray']; const path3: StatePath<State> = ['app', 'testArray', 1, 'firstname']; const path4: StatePath<State> = ['app', 'testArray', 5, 'address', 'doorNumbers', (doorNumber) => doorNumber.number === 1, 'appendixes'];
优化点说明
- 用静态层级替换递归,彻底消除递归类型的重复推导开销,编译速度大幅提升
- 每一层都保留“中途终止路径”的特性
- 数组和对象的分支逻辑清晰,严格遵循需求规则
- 10层后回退为宽松类型,避免过度类型推导影响性能
方案二:优化递归版本(接近无限制层级)
利用TypeScript 4.1+的条件类型延迟求值特性,让递归仅在需要具体推导时展开,而非一次性遍历所有可能路径:
// 断言函数类型定义 export type PredicateFunction<ArrayType> = (item: ArrayType, index?: number) => boolean; export type IndexOrPredicate<Type> = number | PredicateFunction<Type>; // 优化后的递归路径类型 type StatePathRecursive<Obj, Prev extends any[] = []> = // 允许终止在当前层级 Prev | // 仅当Obj是对象时才继续推导,利用延迟求值避免立即展开所有层级 (Obj extends object ? (Obj extends readonly any[] ? // 数组分支:推导下一层路径 StatePathRecursive<Obj[number], [...Prev, IndexOrPredicate<Obj[number]>]> : // 对象分支:遍历所有合法键,推导下一层路径 { [K in keyof Obj & string]: StatePathRecursive<Obj[K], [...Prev, K]> }[keyof Obj & string] ) : never); // 示例使用 const path: StatePathRecursive<State> = ['app', 'testArray', 0, 'address', 'doorNumbers', (d) => d.number === 1];
优化点说明
- 利用条件类型延迟求值,只有当TypeScript需要校验具体路径时才会展开递归,初始编译开销极低
- 理论上支持无限层级(受TypeScript默认递归深度限制,约1000层)
- 完全保留所有需求特性,类型安全性不受影响
内容的提问来源于stack exchange,提问作者Stefan Holzapfel
相关产品推荐
相关产品推荐

