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

TypeScript如何实现已知键名未知路径的深层属性类型提取

原理解析

你的实现本质是深度优先递归遍历对象的所有属性路径,匹配目标键后返回对应类型,最后合并所有匹配结果。关于你疑惑的[keyof T]逻辑,拆解如下:

  1. 第一层条件判断Key extends keyof T优先匹配当前层级的目标键,如果命中直接返回对应类型,终止当前路径的递归。
  2. 如果当前层级没有匹配到目标键,会构造一个临时映射类型:遍历当前对象的所有属性,对每个属性值做判断:
    • 如果属性值是对象类型,就递归调用GetDeepProp深入查找目标键
    • 如果属性值不是对象类型,说明这条路径不可能存在目标键,返回unknown
  3. 末尾的[keyof T]是索引访问操作,会把上述临时映射类型所有属性值的类型取联合。你的示例能正常运行,是因为示例里的所有非目标路径要么不存在,要么全部是嵌套对象最终命中目标键,没有返回unknown的分支干扰结果。
现有实现的隐藏缺陷

你的实现仅在「所有非目标路径都是嵌套对象最终命中目标键」的场景下可用,实际存在几个明显问题:

  • 如果对象存在不包含目标键的非对象属性,最终返回类型会变成unknown。比如以下示例会直接失效:
    type Test = { name: string; account: { region: number } }
    // 实际返回 unknown,预期返回 number
    type TestRegion = GetDeepProp<Test, 'region'>
    
    原因是string | unknown的联合类型结果是unknown,直接覆盖了有效匹配结果。
  • 不支持数组类型的嵌套场景:如果中间路径是数组,你的实现会返回unknown,无法匹配数组元素内的目标键。
  • 目标键不存在时没有明确的错误提示,只会返回unknown,不利于类型校验。
  • 如果多个路径都存在相同的目标键,会返回所有匹配类型的联合,如果你预期仅存在一个匹配路径,这种结果不符合预期。
更优的实现方案

核心优化点是把无匹配路径的返回值从unknown换成never(联合类型中never会被自动消去,不会干扰有效结果),同时增加数组类型的兼容逻辑:

type GetDeepProp<T extends object, Key extends string> = 
  Key extends keyof T 
    ? T[Key]
    : {
      [k in keyof T]: T[k] extends object 
        ? T[k] extends (infer Elem)[] 
          ? GetDeepProp<Elem, Key>
          : GetDeepProp<T[k], Key>
        : never
    }[keyof T]

// 缺陷场景修复验证
type Test = { name: string; account: { region: number } }
type TestRegion = GetDeepProp<Test, 'region'> // 正确返回 number

type ArrayTest = { accounts: { region: "USA" | "China" }[] }
type ArrayRegion = GetDeepProp<ArrayTest, 'region'> // 正确返回 "USA" | "China"

type NonExistKey = GetDeepProp<Test, 'none'> // 不存在的键返回 never,便于校验

如果需要限制递归深度避免无限循环,可以额外增加一个深度控制参数,默认设为3或4层即可覆盖绝大多数业务场景。

内容的提问来源于stack exchange,提问作者Gisheri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 16:57:02