基于对象键的TypeScript递归类型函数实现问题求助
解决TypeScript递归键路径函数的联合类型问题
你的代码在处理多分支对象时失效,核心原因是联合类型的keyof计算逻辑:当T是多个对象的联合类型时,T[keyof T]会变成这些对象值的联合类型,而keyof (A | B)等于keyof A & keyof B——如果A和B的键没有交集,结果就是never,导致后续调用无法传入有效键。
比如valuesB的类型是{a: {aa: ...}, b: {bb: ...}},第一次调用booB('a')时,原代码里的U是T[keyof T]即{aa: ...} | {bb: ...},下一层的keyof U是'aa' & 'bb'也就是never,所以传'aa'会报类型不匹配。
解决方案
修改函数,让每次返回的函数通过泛型参数捕获具体传入的键,从而将子类型从联合类型中精准缩小,而不是保留整个联合类型。调整后的代码如下:
const valuesB = { a: { aa: { aaa: 'hello aaa', } }, b: { bb: { bbb: 'hello bbb', } } } as const; function foo<T>() { return function <K extends keyof T>(key: K) { // 直接用T[K]作为下一层泛型参数,而非T[keyof T] return foo<T[K]>(); }; } const booB = foo<typeof valuesB>(); booB('a')('aa')('aaa'); // 正常工作 booB('b')('bb')('bbb'); // 正常工作 booB('a')('bb'); // 正确报错:类型'"bb"'不能赋值给类型'keyof { aa: { ... } }'
为什么有效?
每次调用返回的函数时,泛型K会捕获你传入的具体键(比如'a'),T[K]会被精准缩小为该键对应的子类型({aa: ...}),而不是整个联合类型。这样下一层函数的keyof T[K]就是该子类型的有效键,不会出现never的情况。
进阶:递归到最后一层返回值
如果需要递归到最后一层直接返回对应的值,而非继续返回函数,可以进一步优化函数,判断当前类型是否为原始类型:
// 辅助类型:判断是否为非空对象类型 type IsNonEmptyObject<T> = T extends null | undefined ? false : T extends object ? true : false; function foo<T>(): IsNonEmptyObject<T> extends true ? <K extends keyof T>(key: K) => typeof foo<T[K]> : T; function foo<T>(obj?: T) { if (obj == null || typeof obj !== 'object') { return obj; } return function <K extends keyof T>(key: K) { return foo(obj[key]); }; } // 测试:可以直接获取最终值 const values = { a: { aa: { aaa: 'hello aaa', } }, b: { bb: 'hello bb', } } as const; const result1 = foo<typeof values>('a')('aa')('aaa'); // 类型为'"hello aaa"' const result2 = foo<typeof values>('b')('bb'); // 类型为'"hello bb"'
内容的提问来源于stack exchange,提问作者Stephan D.
相关产品推荐
相关产品推荐

