ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Type Challenges 实战:用模板字面量类型实现 ParseUrlParams 解析 URL 路径参数

Type Challenges 实战:用模板字面量类型实现 ParseUrlParams 解析 URL 路径参数 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载type-challenges 是一个以在线评测形式训练 TypeScript 类型编程能力的开源仓库包含从 Warm-up 到 Extreme 各难度级别的类型挑战。本题 ParseUrlParams 是其中一道 medium 难度题目要求实现一个类型层面的 URL 路径参数解析器把形如posts/:id/:user的路径字符串解析为参数名的联合类型id | user。读完本文你将掌握模板字面量类型与infer的配合套路理解如何用类型系统递归解析带分隔符的字符串并能在 template.ts 中独立完成作答。一、题目原貌从需求到测试用例题目出自作者 Anderson. JGitHub: andersonjoseph在 info.yml 中被标记为difficulty: medium标签为infer、string、template-literal。原题给出的目标行为非常简洁ParseUrlParams:id // id ParseUrlParamsposts/:id // id ParseUrlParamsposts/:id/:user // id | user也就是说输入是一个包含路径参数占位符以:开头的片段的 URL 路径字符串输出是把所有参数名合并而成的联合类型。题干只给出了三个示例真正的行为边界藏在测试用例里。测试用例定义了全部边界行为仓库中的 test-cases.ts 是这道题唯一的官方验收标准它通过type-challenges/utils提供的Equal与Expect工具类型对答案做严格类型比对import type { Equal, Expect } from type-challenges/utils type cases [ ExpectEqualParseUrlParams, never, ExpectEqualParseUrlParams:id, id, ExpectEqualParseUrlParamsposts/:id, id, ExpectEqualParseUrlParamsposts/:id/, id, ExpectEqualParseUrlParamsposts/:id/:user, id | user, ExpectEqualParseUrlParamsposts/:id/:user/like, id | user, ]从这六条用例可以提炼出完整的行为规格输入路径期望输出行为含义never空字符串没有参数输出空联合never:idid纯参数路径输出参数名posts/:idid静态片段 参数片段posts/:id/id尾部斜杠不影响参数提取posts/:id/:userid | user多个参数合并为联合类型posts/:id/:user/likeid | user参数之间可以夹杂静态片段其中Expect的定义位于 utils/index.d.tsexport type ExpectT extends true T export type EqualX, Y (T() T extends X ? 1 : 2) extends (T() T extends Y ? 1 : 2) ? true : falseEqual采用函数签名对比的经典技巧对联合类型是严格非分配、非交换比对的因此id | user必须原样产出任何顺序或结构偏差都会导致用例失败。而never是空联合恰好可以表示没有任何参数。初始模板只有一个占位类型作答起点 template.ts 中只有一行type ParseUrlParamsT any我们的目标就是把它替换成一个递归的模板字面量类型解析器。二、解法拆解infer 模板字面量类型逐段掐头解决这类字符串解析问题核心武器是 TypeScript 的模板字面量类型Template Literal Types与infer条件推断。整体思路是递归地每次从字符串头部剥掉一小段先用模式匹配判断当前剩余字符串以什么开头再决定是否提取参数名、跳过静态片段直到字符串耗尽。分支一从参数片段开始:id或:id/xxx先用条件类型加infer从头部提取参数名type ParseUrlParamsT T extends ${infer Param}/${infer Rest} ? Param extends :${infer Name} ? Name | ParseUrlParamsRest // 形如 posts/:id/:user : ParseUrlParamsRest // 形如 posts/:id : T extends :${infer Name} ? Name // 形如 :id : never // 形如 或 posts关键点T extends \${infer Param}/${infer Rest}把字符串在第一个/处切开Param是/之前的片段Rest 是之后的剩余部分若Param以:开头通过Param extends \:${infer Name}提取冒号后的名字则产出Name并继续递归解析Rest若Param是静态片段如posts则不产出任何类型直接递归Rest当字符串不再包含/时落入最后的T extends \:${infer Name}分支提取最后一个参数名若连:都没有空串或纯静态串返回never。分支二从静态片段开始posts/:id上面的写法要求T以参数或静态片段 /的形态匹配但对于posts/:id这种以静态片段开头的字符串Param会被推断为postsParam extends \:${infer Name}不成立于是跳过posts继续递归Rest :id——最后一次迭代中:id没有/命中末尾分支提取出id。可见两个分支配合后静态片段、参数片段可以任意交错出现。用测试用例逐步走查以posts/:id/:user/like为例首次匹配Param posts非参数递归:id/:user/likeParam :id→Name id产出id递归:user/likeParam :user→Name user产出user递归likelike不含/、也不以:开头返回never。最终结果id | user | never联合类型会自动吸收never化简为id | user与 test-cases.ts 的期望一致。边界情况验证不匹配/分支也不匹配:分支返回never✅:id无/命中末尾分支返回id✅posts/:id/Param posts递归:id/Param :id产出id递归返回never结果id✅posts/:id如上所述 ✅。更紧凑的写法把:校验合并进第一段另一个常见解法是只对首段做一次匹配用嵌套推断同时检查:前缀type ParseUrlParamsT extends string T extends ${infer Param}/${infer Rest} ? Param extends :${infer Name} ? Name | ParseUrlParamsRest : ParseUrlParamsRest : T extends :${infer Name} ? Name : never这与上一版等价也可以把含/与不含/两种情况统一成一次T extends \${infer Param}/${infer Rest}后在Param上做: 前缀判断但无论哪种写法本质都是分段剥取 前缀判定 递归收敛。一个容易踩的坑不要在:上直接切分新手常见的错误写法是试图按:切分字符串例如T extends \${infer A}:${infer B}。这会导致posts/:id中A posts、B id看似可行但一旦遇到posts/:id/:user/like这类多参数串infer A与infer B的贪婪匹配会让B吞掉后续内容且:前后片段的归属难以稳定控制最终产出的联合类型往往不符合Equal的严格比对。因此务必以/作为分隔符、以: 作为参数前缀标记二者职责分离。三、仓库印证这类题在 type-challenges 中的家族脉络ParseUrlParams 不是孤立的。它的两个标签infer与template-literal是 type-challenges 中字符串类题目的两大支柱仓库里存在大量同族题目可供对照练习StartsWith判断字符串T是否以U开头同样是T extends \${U}${infer Rest}这种头部模式匹配 infer 提取剩余部分的典型应用其测试用例覆盖了abc/ab 等边界EndsWith与 StartsWith 对称把匹配锚点移到字符串尾部对应T extends \${infer Rest}${U}TrimLeft 等字符串裁剪类题目逐字符剥掉空白与逐段剥掉路径片段的思路同源。这些题目共同展示了模板字面量类型的核心能力在infer的配合下类型系统可以像运行时正则一样对字符串做模式匹配、分段与递归解析。而infer的通用语法在条件类型T extends SomeType ? infer X extends ... ? ... : ... : ...中提取子类型正是本仓库 guides/infer.md 所拟讲解的核心主题。四、如何运行与验证作答与验证的完整路径如下阅读题目打开 README.md 与 test-cases.ts明确全部用例实现类型在 template.ts 中把type ParseUrlParamsT any替换为上面的递归实现本地校验在仓库根目录执行npx tsc --noEmit questions/09616-medium-parse-url-params/test-cases.tsTypeScript 版本需支持模板字面量类型即 TS 4.1若所有ExpectEqual...均通过则类型答案正确若某个用例报错说明对应输入的产出类型与期望不一致可结合上文走查步骤定位是哪一段剥取逻辑出了问题。需要注意题目的Equal比对是严格且分配性的多参数场景必须保证联合成员与测试期望完全一致id | user而不是其他组合这也是为什么递归过程中要按顺序逐个产出参数名。五、小结ParseUrlParams 是一道典型的 medium 级字符串解析类型题它把三个关键知识点串了起来模板字面量类型用${infer Param}/${infer Rest}在/处切分字符串infer条件推断从:xxx中提取参数名并把剩余部分交给下一次递归递归 never收敛联合类型天然吸收never使空串场景无需特判即可得到空联合。掌握以分隔符切片、以前缀标记判别、以递归收尾这一套路后你就能举一反三地解决 EndsWith、Split 乃至更复杂的 URL 解析类类型题这也是 type-challenges 中字符串类型编程的基石能力。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐type-challenges 实战用模板字面量类型实现 Typed Get为嵌套路径访问补全类型推断type challenges 实战用模板字面量类型实现 Typed Get为嵌套路径访问补全类型推断 Typed Get 编号 270难度 hard示例工程type-challenges 300 实战用模板字面量类型实现 String to Number 字符串转数字type challenges 300 实战用模板字面量类型实现 String to Number 字符串转数字 本文围绕 type challenges 第示例工程type-challenges 实战用模板字面量类型实现 Replace 字符串替换工具类型type challenges 实战用模板字面量类型实现 Replace 字符串替换工具类型 导读 本篇文章围绕 type challenges 项目中的第示例工程上一篇5个专业技巧让你掌握Upscayl免费开源AI图像放大工具的终极指南下一篇5分钟搞定Windows Web开发环境Wnmp完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表