
示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载Typed Get编号 270难度 hard是 type-challenges 仓库中的一道经典类型体操题lodash 的get在 JavaScript 中访问嵌套值十分便捷但进入 TypeScript 后函数签名会丢掉路径对应的类型信息本题目要求借助 TypeScript 4.1 的 Template Literal Types模板字面量类型实现一个能在编译期根据点分路径解析出目标类型的GetT, K。读完本文你将掌握模板字面量类型的模式匹配、条件类型分发与递归类型拆分的组合用法并理解“字面量键优先于路径拆分”这一关键边界设计。题目背景lodash get 与类型信息的丢失lodash 的get函数允许以字符串路径访问嵌套值例如get(data, foo.bar.count)。它在 JavaScript 层面非常实用但问题在于get的运行时实现无法把路径字符串映射为 TypeScript 类型因此这类 API 的返回值往往退化为any或宽泛的联合类型嵌套对象原本精确的结构信息在调用点全部丢失。这道题的核心诉求正是“夺回”这些类型信息。只要路径字符串是可静态分析的字面量类型如foo.bar.count就能通过模板字面量类型在类型层面把它逐段拆分再结合索引访问T[K]递归下钻最终在编译期推导出目标节点的精确类型。这也是模板字面量类型在“字符串即数据”场景中最典型的一类应用——把字符串当作一条结构化路径来解析。题面给出了目标行为示例type Data { foo: { bar: { value: foobar; count: 6; }; included: true; }; hello: world; }; type A GetData, hello; // world type B GetData, foo.bar.count; // 6 type C GetData, foo.bar; // { value: foobar, count: 6 }并明确说明本题不需要处理数组下标访问题面原文“この課題では、配列へのアクセスは必要ありません”路径仅由对象属性名以.连接而成。题目信息与初始模板该题在仓库中位于questions/00270-hard-typed-get/目录从 info.yml 可以确认它的元数据难度hard标签utils、template-literal工具类 模板字面量主题作者Anthony Futemplate.ts 中给出的待填空模板极其精简type GetT, K string也就是说起点是一个把所有输入都映射为string的占位实现你需要把它重写为能够按路径推导类型的版本。测试用例拆解比题面更严苛的边界题面只给了三条正向示例但真正的约束藏在 test-cases.ts 里。它使用type-challenges/utils仓库内的 utils/index.d.ts提供的Equal与Expect做类型级断言共五条用例type cases [ ExpectEqualGetData, hello, world, ExpectEqualGetData, foo.bar.count, 6, ExpectEqualGetData, foo.bar, { value: foobar, count: 6 }, ExpectEqualGetData, foo.baz, false, ExpectEqualGetData, no.existed, never, ]; type Data { foo: { bar: { value: foobar count: 6 } included: true } foo.baz: false hello: world }注意这里比题面多了两条关键约束GetData, foo.baz必须返回false。Data中恰好存在一个字面量键foo.baz键名本身包含点号其值为false。这条用例要求当完整路径本身就是一个真实键时必须优先按字面量键直接索引而不能把它当作foo→baz的嵌套路径去拆分否则会走Data[foo]之后找不到baz结果退化为never。GetData, no.existed必须返回never。对于完全不存在既不是字面量键、也无法逐段命中的路径结果应为never而不是报错或返回any。这要求实现对“路径失效”有明确的兜底分支。核心实现递归拆分 索引访问把Get写成一个先查整键、再递归拆分的类型即可同时满足上述全部用例type GetT, K extends string K extends keyof T ? T[K] : K extends ${infer Head}.${infer Tail} ? Head extends keyof T ? GetT[Head], Tail : never : never逐行解读这条类型的执行逻辑第一分支K extends keyof T先检查完整路径字符串是否直接就是T的某个键。这覆盖了hello、foo.bar字面量键等“整串命中”的情况——尤其是foo.bar这条用例正是靠这个分支把结果锁定为false而不是错误地进入拆分逻辑。**第二分支K extends \${infer Head}.${infer Tail}**当整串不是键时利用模板字面量类型的模式匹配把K在第一个.处拆为Head首段键名与Tail剩余路径。例如foo.bar.count会拆出Head foo、Tail bar.count。Head extends keyof T ? GetT[Head], Tail : never首段必须命中当前对象的键命中则对T[Head]用剩余路径Tail递归调用Get实现逐层下钻未命中则说明路径在某一层断掉了返回never。最终兜底: never如果K既不是键、也拆不出.说明它是不存在的孤立键名同样返回never。用五条用例验证一遍输入路径执行轨迹结果hello命中整键 →Data[hello]worldfoo.bar.count拆分 →Data[foo]→ 递归拆bar.count→Data[foo][bar]→ 递归count→ 整键命中6foo.bar整键不命中 → 拆分foobar→ 递归Getbar对象, bar→ 整键命中{ value: foobar, count: 6 }foo.baz整键命中字面量键优先→Data[foo.baz]falseno.existed整键不命中 → 拆分noexisted→no不是键 → 兜底never关键设计点在于整键检查必须放在模式匹配拆分之前。若颠倒顺序foo.baz会先被拆成foo与baz随后在Data[foo]一个不含baz的对象中找不到baz而返回never与测试用例ExpectEqualGetData, foo.baz, false直接冲突。这一“字面量键优先”的顺序本质上是把字符串键的两种语义——字面量键名与路径表达式——做了明确的优先级约定。实现变体用默认泛型参数强化边界另一个常见变体是利用泛型默认参数在递归时把失败信号显式传递type GetT, K extends string, Fallback never K extends keyof T ? T[K] : K extends ${infer Head}.${infer Tail} ? Head extends keyof T ? GetT[Head], Tail, Fallback : Fallback : Fallback这个版本把“路径失效”的兜底值抽象成第三个泛型参数默认never行为与标准解法一致若将来想“路径不存在时返回用户指定的默认值”只需显式传入第三个参数即可可读性略好但泛化程度更高。两种写法都能通过test-cases.ts的全部断言。底层原理模板字面量类型的模式匹配这道题能成立完全依赖 TypeScript 4.1 引入的模板字面量类型。其核心能力是在K extends \${infer Head}.${infer Tail}这样的条件类型中编译器会把字面量字符串按模板结构进行**模式匹配与推断**——.前的内容推断为Head.后的内容推断为Tail从而把运行时字符串的“split 逻辑”搬到了类型层面。这正是题目标签template-literal 的含义。配合使用的另外两个机制是条件类型分发K extends ... ? ... : ...会基于真实字符串字面量逐条计算保证路径是精确的字面量类型而非宽泛的string若K是string推断出的Head/Tail也会退化为string无法索引。递归类型引用GetT[Head], Tail允许类型在自身定义中引用自己把“逐段下钻”表达为栈式递归每次递归减少一段路径最终收敛到整键命中的基线分支。本仓库中 guides/recursive.md 与 guides/key-in.md 也分别以 TODO 形式预留了递归与键操作专题的扩展资料可见这两个主题是该系列挑战反复出现的核心技法。需要留意的是递归类型依赖编译器设定的类型实例化深度上限默认约 50 层因此对于超深层级对象或超长路径Get存在递归深度限制——这是所有基于递归拆分的类型体操共同的约束对本题的正常用例三层嵌套完全够用。总结Typed Get的价值在于把 lodashget丢失的类型信息重新“焊接”回调用点通过模板字面量类型的模式匹配把点分路径逐段拆分借助条件类型的整键优先分支处理“键名含点”的边界情况再用递归类型逐层下钻直至命中目标类型。最终产物是一个由 template.ts 中一行占位type GetT, K string演化而来的精确路径索引器且全部行为都有 test-cases.ts 中的Equal/Expect断言背书。若想亲手验证或继续深入可基于 test-cases.ts 中的Data结构替换为自己的嵌套对象观察不同路径分支的推导结果进一步地还可以把Get的兜底值泛型化、扩展数组下标访问题面明确不要求但可作为延伸练习、或与Partial、Readonly等工具类型组合构建更完整的类型安全数据访问层。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐Front-End Checklist 中的 const/let 规则告别 var用块级作用域与 lint 消除提升导致的隐患Front End Checklist 中的 const/let 规则告别 var用块级作用域与 lint 消除提升导致的隐患 本文围绕 Front End示例工程type-challenges 题解 00529用模板字面量类型实现 Absolute 绝对值类型type challenges 题解 00529用模板字面量类型实现 Absolute 绝对值类型 本篇文章围绕 type challenges 仓库中的中等示例工程RIOT STM32 时钟树配置详解从 SYSCLK 源选择到 PLL、APB 与 MCO 参数的完整实践指南RIOT STM32 时钟树配置详解从 SYSCLK 源选择到 PLL、APB 与 MCO 参数的完整实践指南 本文以 RIOT 仓库中 STM32 CPU示例工程上一篇llama.cpp 移动端部署4 步把大模型搬进 Android 与 iOS下一篇pidcat完全教程从安装到高级过滤的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考