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

基于对象键的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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 20:30:43