
示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载导读PercentageParser是 Type Challengestype-challenges题库中编号 1978 的中等难度挑战Medium标签#template-literal其核心任务是仅用类型系统实现一个符合正则/^(\|\-)?(\d*)?(\%)?$/的百分比字符串解析器把形如85%、-100、85%的字符串字面量类型拆解为[正负号, 数字, 单位]三元组。读完本文你将掌握模板字面量类型Template Literal Types与infer模式匹配的组合技巧能够独立写出通过全部 12 个官方测试用例的正确答案并理解此类“字符串解析”类型题目的通用解题框架。题目背景type-challenges 题库中的 1978 号挑战本项目是一个 TypeScript 类型挑战的在线评测仓库Collection of TypeScript type challenges with online judge每一道题由一个题目目录构成目录内通常包含四类文件README.md及多语言版本如 README.zh-CN.md、README.ko.md题目描述、难度徽章、在线挑战入口template.ts预置的类型占位符等待答题者填充test-cases.ts官方测试用例用于验证你的实现info.yml题目元信息标题、作者、标签、难度等。本题 Percentage Parser 由 SSShuai1999 提出被标记为中等难度medium属于#template-literal模板字面量主题系列——与TrimLeft、Trim、Capitalize、String to Number等题目同属一个知识簇。题目目录中的初始模板只有一行占位符// questions/01978-medium-percentage-parser/template.ts type PercentageParserA extends string any你的任务就是把any替换为真正的类型逻辑使其满足题目描述与全部测试用例。题目要求与正则规则逐段解读官方需求原文ImplementPercentageParserT extends string. According to the/^(\|\-)?(\d*)?(\%)?$/regularity to match T and get three matches. The structure should be: [plus or minus,number,unit] If it is not captured, the default is an empty string.即实现PercentageParserT extends string按照正则/^(\|\-)?(\d*)?(\%)?$/匹配类型T输出结构为[正负号, 数字, 单位]的三元组某个分组未匹配到时默认取空字符串。正则语义拆解正则片段含义匹配示例不匹配^锚定字符串开头——(\|\-)?可选的或-符号85%、-8585%无符号(\d*)?零个或多个数字\d*本身允许空外层再包一层可选组85、1%无数字(\%)?可选的百分号%85%85无单位$锚定字符串结尾——注意两个关键点所有分组都是可选的因此空字符串、单独的、单独的%都是合法输入此时未捕获的部分输出为空字符串数字部分只能是纯数字串正则不处理小数、千分位等格式也不要求数字必须存在——(\d*)?中的*允许匹配 0 位数字。官方示例题目 README 给出的 5 个示例与期望结果如下type PString1 // type R1 PercentageParserPString1 // [, , ] type PString2 85% // type R2 PercentageParserPString2 // [, 85, %] type PString3 -85% // type R3 PercentageParserPString3 // [-, 85, %] type PString4 85% // type R4 PercentageParserPString4 // [, 85, %] type PString5 85 // type R5 PercentageParserPString5 // [, 85, ]测试用例全量分析12 组输入与期望题目仓库中的 test-cases.ts 比 README 示例覆盖更全共 12 个用例完整列举如下import type { Equal, Expect } from type-challenges/utils 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, ]整理成表格便于对照用例输入期望[符号, 数字, 单位]覆盖场景Case0[, , ]空字符串三分组全部缺省Case1[, , ]只有符号无数字无单位Case21[, 1, ]符号 单数字Case3100[, 100, ]符号 多数字Case4100%[, 100, %]三分组齐全Case5100%[, 100, %]无符号数字 单位Case6-100%[-, 100, %]负号 数字 单位Case7-100[-, 100, ]负号 数字Case8-1[-, 1, ]负号 单数字Case9%[, , %]只有单位无符号无数字Case101[, 1, ]只有数字Case11100[, 100, ]纯数字串可以看到12 个用例构成了对符号 × 数字 × 单位组合空间的近乎穷举覆盖任何只处理“标准形式±数字%”的偷懒实现都会在 Case0、Case1、Case9 上失败。这些用例使用type-challenges/utils见 utils/index.d.ts导出的Equal与Expect进行严格的结构相等性断言// utils/index.d.ts export type EqualX, Y (T() T extends X ? 1 : 2) extends (T() T extends Y ? 1 : 2) ? true : falseEqual通过函数签名可赋值性反演判断两个类型是否完全一致能区分any、never、联合类型等微妙差异比简单的extends判断严格得多。解法一基于infer的单链模式匹配推荐模板字面量类型的核心能力是在${...}模板中通过infer捕获子串。我们可以用“先剥符号、再剥单位”的两步推断完成解析type PercentageParserA extends string A extends ${infer Sign extends | -}${infer Rest} ? Rest extends ${infer Num}% ? [Sign, Num, %] : [Sign, Rest, ] : A extends ${infer Num}% ? [, Num, %] : [, A, ]执行逻辑逐步拆解第一步尝试剥离符号。A extends \${infer Sign extends | -}${infer Rest}要求A的首字符是或-。infer Sign extends ...是 TypeScript 4.7 引入的 infer 类型约束语法它强制推断结果必须满足 | -从而保证首字符被正确识别为符号、剩余部分落入Rest输入85%Sign Rest 85%输入-100Sign -Rest 100输入85%首字符是数字不满足约束整个分支失败进入第三步。第二步符号分支内尝试剥离单位%。对Rest再做一次${infer Num}%匹配85%匹配成功 →[Sign, 85, %]100不匹配结尾不是%→[Sign, 100, ]即输入为时也不匹配 →[, , ]正确覆盖 Case1。第三步无符号分支。若第一步失败说明字符串不以/-开头此时再判断是否以%结尾100%→[, 100, %]Case5%→${infer Num}%中Num被推断为空串 →[, , %]Case91、100→ 不以%结尾走兜底[, A, ]Case10、Case11→ 两个分支都不匹配兜底为[, , ]Case0。这个实现能通过全部 12 个用例Case4、Case6、Case2、Case3、Case8、Case7 走第一、二步Case5、Case9 走第三步的%分支Case0、Case1、Case10、Case11 走各自兜底分支。解法二拆分辅助类型的“三段式”实现如果你觉得单链推断可读性不够可以像处理管道一样把解析拆成三个独立的小工具类型思路与正则的三分组一一对应// 1. 提取符号返回 [符号, 剩余部分] type ParseSignS extends string S extends ${infer Sign extends | -}${infer Rest} ? [Sign, Rest] : [, S] // 2. 提取单位返回 [数字, 单位] type ParseUnitS extends string S extends ${infer Num}% ? [Num, %] : [S, ] // 3. 组装 type PercentageParserA extends string ParseSignA extends [infer Sign, infer Rest] ? ParseUnitRest string extends [infer Num, infer Unit] ? [Sign, Num, Unit] : never : never逐步验证输入-100%ParseSign得到[-, 100%]ParseUnit100%得到[100, %]组装为[-, 100, %]✓输入ParseSign得到[, ]ParseUnit匹配${infer Num}%失败 →[, ]组装为[, , ]✓输入85%ParseSign失败 →[, 85%]ParseUnit85%→[85, %]组装为[, 85, %]✓输入ParseSign→[, ]ParseUnit→[, ]组装为[, , ]✓第二种写法把“符号剥离”与“单位剥离”解耦逻辑更接近题目给出的正则分组结构后续想扩展比如支持85.5%或0.85时只需修改对应的辅助类型即可。边界情况与易错点分析这道题真正考察的是对可选分组缺省语义的把握以下几个点最容易写错只有符号初学者常误以为“有符号必有数字”写出先剥符号、再直接取Rest作为数字的实现导致结果变成[, , ...]以外的形式。正确做法是让Rest为空串时自然落进兜底分支。%只有单位输入以%开头符号分支失败在${infer Num}%匹配中Num被推断为空串此时必须显式返回[, , %]而非把空串误判为“无单位”。空字符串一切匹配都失败需要最终的兜底分支返回[, , ]直接extends判断A extends 也能单独处理但兜底分支更简洁。infer的贪婪性模板字面量推断默认是贪婪的infer Sign会尽量吃下更多字符。因此在/-场景必须加上extends | -约束否则Sign可能吞掉整个字符串。\d*是“零或多个”正则允许没有数字但实际输入中的数字串必须是连续数字模板推断天然满足这一点不需要额外校验。底层原理模板字面量类型 infer 约束要理解这道题的“标准解”需要明白两个语言特性本项目tsconfig.base.json使用lib: [ESNext]、TypeScript^5.3.3完整支持下述特性模板字面量类型Template Literal Types${infer X}${infer Y}允许在类型层面把字符串字面量按结构拆解编译器会在两个infer占位符之间分配字符其能力对应运行时字符串的模式匹配。本项目同类题目如 TrimLeft、Trim、Capitalize 都依赖这一特性。infer X extends Constraint约束推断TypeScript 4.7 起infer可以带extends约束编译器只接受满足约束的推断结果不满足则整个匹配分支失败。这正是解法一能准确区分“首字符是符号”与“首字符是数字”的关键——若没有该语法85%会被infer Sign整体吞掉不得不改用其他 hack。另外注意A extends string的泛型约束模板字面量类型只能作用于string类型的子类型泛型声明中加上extends string是编译期保证安全性的第一道防线。验证方式在仓库中运行测试你可以在本地仓库中完成并验证这道题填写实现编辑 template.ts将any替换为上述任一实现运行类型检查仓库根目录的 package.json 声明了typescript: ^5.3.3与type-challenges/utils: workspace:*utils 包定义见 utils/package.json。使用 pnpm 安装依赖后执行pnpm install npx tsc --noEmit questions/01978-medium-percentage-parser/test-cases.ts若test-cases.ts中的ExpectEqual...全部通过无类型错误输出说明实现正确反之tsc会报告类型不兼容的具体用例。在线评测也可以打开题目目录中的在线挑战链接见 README 顶部的 “Take the Challenge” 徽章进行即时评测并在结束后查看其他参与者的解答。总结一套可复用的“字符串三明治解析”框架回顾整道题PercentageParser的价值不只是“把一个字符串拆成三段”而是演示了类型层面字符串解析的通用套路从外层向里层逐层剥离先处理符号这类“前缀”再处理%这类“后缀”最后剩下的就是核心内容每个分组都可选对每个可空分组必须有明确的“缺失即空串”兜底用infer extends做精确的字符类别判断它把正则的字符类如[-]翻译成了类型约束用穷举测试用例兜底仓库的 12 个用例覆盖了所有组合缺省情况是实现正确性的最终裁判。掌握这套思路后你可以顺手迁移到同仓库的 TrimLeft、String to Number、Absolute 等题目它们共享同一套模板字面量 infer的解析语言——类型系统不是只能描述数据形状它同样是一台能力完备的“字符串处理机”。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐TypeScript 类型挑战type-challenges 题 112 之 CapitalizeWords 模板字面量类型实战解析TypeScript 类型挑战type challenges 题 112 之 CapitalizeWords 模板字面量类型实战解析 本文以 question示例工程TypeScript 类型挑战题解 114用模板字面量类型实现 CamelCase 字符串转换TypeScript 类型挑战题解 114用模板字面量类型实现 CamelCase 字符串转换 导读 本题来自 type challenges 挑战仓库编号示例工程TypeScript 类型挑战 529用模板字面量类型实现 Absolute 绝对值类型number / string / bigintTypeScript 类型挑战 529用模板字面量类型实现 Absolute 绝对值类型number / string / bigint 本篇文章以 ty示例工程上一篇深入AMD处理器内核SMUDebugTool硬件调试完全指南下一篇ncmdumpGUI终极教程3分钟掌握网易云音乐NCM文件转换技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考