多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

用模板字面量类型实现 Percentage Parser:在 type-challenges 中解析正负号、数字与百分号单位

用模板字面量类型实现 Percentage Parser:在 type-challenges 中解析正负号、数字与百分号单位 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载Percentage Parser编号 1978是 type-challenges 仓库中一道中等medium难度、归属于template-literal标签的类型体操题目其核心任务是把85%这类百分比字符串在类型层面拆解为[, 85, %]三段。本文将围绕该题目的完整规则、正则语义、三种可行实现方案、仓库内的测试验证机制与相关知识点展开帮助读者掌握用模板字面量类型 infer做字符串解析的通用方法。题目要求输出一个三元组题目定义在 questions/01978-medium-percentage-parser/README.ko.md英文版见 README.md。需要实现type PercentageParserT extends string它依据正则/^(\|\-)?(\d*)?(\%)?$/匹配字符串T输出结构为元组[正负号, 数字, 单位]第一个元素可选的或-符号第二个元素可选的数字部分\d*允许为空第三个元素可选的%单位。若某个部分未能匹配则对应元素默认为空字符串。难度信息记录在 info.ymldifficulty: medium标签template-literal。题目给出的五个基准示例type PString1 type PString2 85% type PString3 -85% type PString4 85% type PString5 85 type R1 PercentageParserPString1 // expected [, , ] type R2 PercentageParserPString2 // expected [, 85, %] type R3 PercentageParserPString3 // expected [-, 85, %] type R4 PercentageParserPString4 // expected [, 85, %] type R5 PercentageParserPString5 // expected [, 85, ]注意R4与R5当字符串没有正负号时第一项必须是当字符串没有%时第三项必须是。剖析正则/^(\|\-)?(\d*)?(\%)?$/正则本身是题目的语法规范逐段拆解如下片段含义在类型系统里的对应物^从字符串开头匹配模板字面量匹配天然从头开始(\|\-)?可选的或-捕获组 1S extends${infer Sign extends -}${infer Rest}(\d*)?可选的零个或多个数字捕获组 2匹配infer出的数字段需extends \${number} 校验(\%)?可选的%捕获组 3Rest extends${infer Num}%$匹配到字符串结尾模板字面量匹配到剩余部分为空即代表结尾三个捕获组全部可空因此PercentageParser必须处理「空字符串」这个最极端的输入应得到[, , ]这在 test-cases.ts 中被列为Case0。解法一单次条件类型 infer 一次性拆解最直观的思路是只用一个条件类型通过模板字面量一次把三段infer出来type PercentageParserT extends string T extends ${infer Sign extends | -}${infer Rest} ? Rest extends ${infer Num extends ${number}}${infer Unit} ? Unit extends % ? [Sign, Num, %] : Unit extends ? [Sign, Num, ] : [Sign, Num, Unit] : Rest extends % ? [Sign, , %] : [Sign, , ] : T extends ${infer Num extends ${number}}${infer Unit} ? Unit extends % ? [, Num, %] : Unit extends ? [, Num, ] : [, Num, Unit] : T extends % ? [, , %] : [, , ]执行流程先用${infer Sign extends | -}${infer Rest}探测是否以/-开头Sign的约束会保证只吸入单个符号字符若存在符号继续对Rest匹配${infer Num extends ${number}}${infer Unit}其中Num extends${number} 让类型系统只接受数字字面量序列对Unit判断是%、还是其他字符从而决定数字段是否合法无符号分支同理最后兜底处理纯%与空串。该写法把正则的三段捕获映射为三个infer位置逻辑最贴近题目描述但分支较多可读性一般。解法二符号、数字、单位三部分各自独立解析更工程化的做法是把三个捕获组拆成三个独立的小工具类型最后组合成元组便于单独测试// 只解析正负号 type ParseSignT extends string T extends ${infer Sign extends | -}${infer _Rest} ? Sign : // 只解析数字段借助一个剥离 % 的中间类型 type ParseNumberT extends string T extends ${infer _Sign extends | -}${infer Rest} ? ParseNumberRest : T extends ${infer Num}% ? Num : T // 只解析单位 type ParseUnitT extends string T extends ${string}% ? % : type PercentageParserT extends string [ ParseSignT, ParseNumberT, ParseUnitT, ]这种写法把「每个捕获组可空、缺省为空字符串」的规则分别落实在三个工具里组合层只是拼接元组实现和排错都更清晰也更容易被其他题目复用例如把ParseSign单独用于绝对值类工具。解法三递归剥离 单字符消费对于希望避免infer extends高级约束、追求最基础语法的读者可以退回到「一次剥一个字符」的递归风格思路与仓库内Trim、Drop Char等模板字面量题一致type PercentageParserT extends string T extends ${infer Rest} ? [, ...PercentageParserRest] // 剥掉 后递归 : T extends -${infer Rest} ? [-, ...PercentageParserRest] : T extends ${infer Num}% ? [, Num, %] : T extends ${infer Num} ? [, Num, ] : [, , ]不过要注意该写法依赖递归输入较长时会产生更深的实例化而解法一、二均为常量层级的匹配实例化代价更低。从 test-cases.ts 的用例规模最长输入100%看三种写法都能通过。用仓库测试验证12 个断言与 Equal 原理题目的可验证性由 test-cases.ts 保证它覆盖了远比题目 README 示例更全的 12 组输入type Case0 [, , ] type Case1 [, , ] type Case2 [, 1, ] type Case3 [, 100, ] type Case4 [, 100, %] type Case5 [, 100, %] type Case6 [-, 100, %] type Case7 [-, 100, ] type Case8 [-, 1, ] type Case9 [, , %] type Case10 [, 1, ] type Case11 [, 100, ] type cases [ ExpectEqualPercentageParser, Case0, ExpectEqualPercentageParser, Case1, ExpectEqualPercentageParser1, Case2, ExpectEqualPercentageParser100, Case3, ExpectEqualPercentageParser100%, Case4, ExpectEqualPercentageParser100%, Case5, ExpectEqualPercentageParser-100%, Case6, ExpectEqualPercentageParser-100, Case7, ExpectEqualPercentageParser-1, Case8, ExpectEqualPercentageParser%, Case9, ExpectEqualPercentageParser1, Case10, ExpectEqualPercentageParser100, Case11, ]这 12 个用例包含了题目 README 中 5 个示例之外的边界单独一个、单独一个%、两位数与三位数、无数字等。也就是说实现必须让每一个输出元素都有且仅有三种取值之一符号/数字/%/空串不能出现[, 100%, ]这类把%混入数字段的错误。Expect与Equal来自type-challenges/utils其定义位于 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通过「两个函数类型是否互相可赋值」来判断X与Y是否完全一致能捕获联合类型展开、any污染等extends判断不出来差异。ExpectT extends true则保证最终cases数组中每一项都是true字面量——任何一项不满足都会使整个文件编译失败。将PercentageParser的实现填入 template.ts初始为type PercentageParserA extends string any后即可用项目根目录的 tsconfig.json继承自 tsconfig.base.json开启strict: true跑编译验证。本地运行与相关知识延伸本仓库的题目可通过pnpm install后运行generate脚本在本地生成可编译的挑战文件详见 README.md 的 Play Locally 一节也支持直接在 TypeScript Playground 中作答。挑战统一在 strict 模式 下运行因此写类型时不能依赖any逃逸或隐式 any。Percentage Parser 在template-literal标签下与多条姊妹题共享同一套「模板字面量 infer 条件类型」技法Trim Left 与 Trim首尾空白字符的剥离Replace / ReplaceAll、[questions/00119-medium-replaceall/README.md)字符串子串替换Drop Char 与 Split字符剔除与分割Parse URL Params从模板字符串里抽取动态参数。它们的共同点正是本题的核心在类型层面把字符串当作字符序列用${infer}进行模式匹配用约束如infer N extends \${number}收窄匹配范围。小结实现PercentageParserT extends string的本质是把正则/^(\|\-)?(\d*)?(\%)?$/的三个可空捕获组逐一翻译为类型层面的模式匹配并保证缺省输出空字符串。本文给出了单次拆解、三工具组合与递归剥离三种实现其中「按捕获组拆分 组合元组」的结构最利于理解与复用。完成后用 test-cases.ts 中的 12 个用例配合Equal做严格校验即可确认实现满足全部边界条件。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐用模板字面量类型实现 Percentage Parsertype-challenges 第 1978 题「Percentage Parser」深度解析用模板字面量类型实现 Percentage Parsertype challenges 第 1978 题「Percentage Parser」深度解析 本文围示例工程type-challenges 第 25747 题 IsNegativeNumber用模板字面量类型实现数字符号判断type challenges 第 25747 题 IsNegativeNumber用模板字面量类型实现数字符号判断 导读 IsNegativeNumber示例工程type-challenges 中等题精解用模板字面量类型实现 CapitalizeTtype challenges 中等题精解用模板字面量类型实现 CapitalizeT 本篇文章基于开源仓库 type challengesCollect示例工程上一篇YTDLnis终极搜索指南堆叠搜索与批量下载的效率革命下一篇告别模糊标注MMPose集成Albumentations实现工业级数据增强创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表