如何实现提取嵌套对象所有键的TypeScript联合类型?
解决TypeScript提取嵌套对象所有键的联合类型问题
问题分析
你尝试的两种递归实现存在以下问题:
- ExtractKeys无限递归:在TS4.5.5版本中,
T extends Record<infer U, any>的条件依赖键类型推断,当处理嵌套的联合值类型时,TypeScript的类型系统会反复展开联合分支,超出编译器递归深度限制,触发TS2589错误。 - ExtractKeys2类型不准确:递归调用时误写为
ExtractKeys而非ExtractKeys2,导致后续递归逻辑断裂,最终类型不符合预期。
正确实现方案
基础版(包含所有对象类型,包括数组)
// 示例类型,期望得到 'a' | 'b' | 'x' | 'c' | 'd' type O = Record<'a', Record<'b' | 'x', Record<'c' | 'd', string | number>>>; // 递归提取所有嵌套键的联合类型 type ExtractKeys<T> = T extends object ? keyof T | ExtractKeys<T[keyof T]> : never; // 验证:类型为 'a' | 'b' | 'x' | 'c' | 'd' const validKey: ExtractKeys<O> = 'a'; // 正常 // const invalidKey: ExtractKeys<O> = 'z'; // 报错,符合预期
排除数组的版本(仅提取普通对象的键)
如果不需要提取数组的键(如length、数字索引等),可以增加数组过滤逻辑:
type ExtractKeys<T> = T extends any[] ? never : T extends object ? keyof T | ExtractKeys<T[keyof T]> : never;
修正ExtractKeys2的笔误
将递归调用改为当前类型即可修复:
type ExtractKeys2<T> = T extends Record<string, any> ? keyof T | ExtractKeys2<T[keyof T]> : // 修正为ExtractKeys2 never; // 验证:类型正确 // const tryIt2: ExtractKeys2<O> = 'z'; // 报错,符合预期
核心思路
- 用
T extends object替代特定的Record判断,让类型系统更清晰地识别终止条件(遇到非对象类型时返回never)。 - 确保递归调用的是当前定义的类型,避免笔误导致逻辑断裂。
- 按需过滤数组等特殊对象类型,满足不同场景需求。
内容的提问来源于stack exchange,提问作者NoBullsh1t
相关产品推荐
相关产品推荐

