ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

TypeScript 类型体操实战:用模板字面量类型实现 `Trim<T>` 去除两端空白

2026/9/30 8:13:34 拓冰建站 浏览量
TypeScript 类型体操实战:用模板字面量类型实现 `Trim<T>` 去除两端空白 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载导读TrimT是 type-challenges 仓库中一道中等难度medium、标签为template-literal的类型挑战要求实现一个接收精确字符串字面量类型并返回去除两端空白后新字符串类型的工具类型。本文将以 questions/00108-medium-trim/README.md 为骨架结合仓库内的 template.ts 起点、test-cases.ts 测试用例以及同系列的 TrimLeft / TrimRight 姊妹题完整讲解递归模板字面量类型的推导原理、实现步骤与边界情况处理帮助读者掌握字符串类型层面的裁剪能力。挑战速览题目要求题目背景本题由 Anthony Fuantfu提出收录于 questions/00108-medium-trim/ 目录下其元信息在 info.yml 中标明difficulty: medium title: Trim tags: template-literal author: github: antfu name: Anthony Fu related: 106, 4803related: 106, 4803指向两道姊妹题106・Trim Left仅去除左侧空白4803・Trim Right仅去除右侧空白。它们共同构成了字符串空白处理的三件套建议按106 → 108 → 4803的顺序循序渐进。核心需求实现TrimT它接受一个明确的字符串类型exact string type即字符串字面量类型而非string宽类型并返回一个新字符串类型其中两端的空白符都已被删除。题目给出的期望示例type trimmed Trim Hello World // expected to be Hello World验收标准测试用例仓库在每个题目目录下都提供了由type-challenges/utils的Equal与Expect封装的类型级断言测试位于 questions/00108-medium-trim/test-cases.tsimport type { Equal, Expect } from type-challenges/utils type cases [ ExpectEqualTrimstr, str, // 无空白 ExpectEqualTrim str, str, // 单个前导空格 ExpectEqualTrim str, str, // 多个前导空格 ExpectEqualTrimstr , str, // 多个尾随空格 ExpectEqualTrim str , str, // 两端均有空格 ExpectEqualTrim \n\t foo bar \t, foo bar, // 制表符与换行符 ExpectEqualTrim, , // 空字符串 ExpectEqualTrim \n\t , , // 纯空白字符串 ]从用例可以提炼出必须满足的语义边界场景输入期望输出说明无空白strstr原样返回前导空白 strstr多个空格递归剥除尾随空白str str多个空格递归剥除混合空白 \n\t foo bar \tfoo bar需覆盖空格、\n、\t三种字符空字符串递归终止不能报错纯空白字符串 \n\t 空白被完全剥除后落空这里的Equal来自仓库的 utils/index.d.ts是一个基于函数类型推断实现的结构相等判断工具ExpectT extends true T则用于在类型层面强制断言成立。空白字符集合Whitespace联合类型类型级trim与运行时String.prototype.trim()一样需要明确哪些字符算空白。题目测试用例覆盖了三种字符空格 、换行\n、制表符\t见 \n\t foo bar \t用例。因此第一步是定义空白字符的联合类型type Whitespace | \n | \t这是一个关键的建模决策把空白建模为有限字符联合而不是单个字符。这样做的直接好处是在与模板字面量进行模式匹配时infer可以自由匹配联合中的任一成员从而一次覆盖所有空白字符。说明本题的官方测试集只要求覆盖 、\n、\t三种。若你的实现希望更通用可扩展联合例如\r回车乃至\u3000全角空格但必须在满足本仓库测试用例的前提下进行不要为了更全面而引入题外复杂度。逐层剥离模板字面量的条件递归核心模式${infer Head}${Tail}拆解模板字面量类型Template Literal Type在infer配合下可以解构字符串的首个字符type FirstCharS extends string S extends ${infer Head}${string} ? Head : never同样的机制用于递归剥除左侧空白只要字符串的首字符是空白字符就将其丢弃并继续对剩余部分递归处理type TrimLeftS extends string S extends ${Whitespace}${infer Rest} ? TrimLeftRest : S推导过程可以用TrimLeft \n\t foo bar 拆解为若干步 \n\t foo bar 匹配${ }${ \n\t foo bar }递归依次剥除第二个、第三个空格以及\n、\t剩余 foo bar 的首字符是 继续剥除剩余foo bar 首字符f不是空白走: S分支返回foo bar 。该实现的正确性可以由姊妹题 questions/00106-medium-trimleft/test-cases.ts 中的用例验证其中包括TrimLeft str → str 与TrimLeft \n\t foo bar → foo bar 。对称处理右侧尾递归到头部右侧空白无法像左侧那样直接拆首字符但可以把问题翻转不去看字符串末尾而是不断拆掉非末尾部分让右侧空白逐步暴露到字符串首部。具体做法是让字符串匹配${infer Rest}${Whitespace}末字符为空白然后将前面部分Rest递归传入直到末尾不再是空白type TrimRightS extends string S extends ${infer Rest}${Whitespace} ? TrimRightRest : S推导TrimRight foo bar \n\t 末字符 是空白拆出 foo bar \n\t递归依次剥除\n、\t、末尾空格、空格剩余 foo bar末字符r非空白返回。对应用例见 questions/04803-medium-trim-right/test-cases.ts其中TrimRight str → str验证了右侧剥离、左侧保留的语义。组合成 Trim两次递归的串联TrimT的本质是先执行 TrimLeft、再执行 TrimRight。最直白的写法是组合两个已实现的工具类型type TrimS extends string TrimLeftTrimRightS或先左后右、结果一致type TrimS extends string TrimRightTrimLeftS这里需要思考一个问题为什么两次递归叠加不会破坏已剥净的一侧关键在于TrimLeft只检查首字符、TrimRight只检查末字符二者互不干扰——左侧剥完后首字符必然非空白TrimRight不会误伤它反之亦然。关于实现风格的补充说明若不想复用姊妹类型也可以写成一个单一递归的版本交替剥除两端空白直到首尾均非空白才终止。这类实现可行但可读性通常不如左右组合清晰。模板字面量类型在 TS 4.1 起可用递归类型实例化深度受编译器限制对这类逐字符剥除的短字符串场景完全足够。本仓库 package.json 中 devDependencies 声明typescript: ^5.3.3在该版本下上述写法均能正常通过测试。条件类型在裸类型参数上会触发分发distributive而S extends string后TrimLeftTrimRightS中的S是明确的字面量不涉及分发问题但为严谨起见工具类型对外仍建议保留S extends string约束。类型级 trim 的意义与适用边界适用场景运行时String.prototype.trim()处理的是值而TrimT处理的是类型。当字符串字面量类型进入类型系统例如被as const断言、或被用作字面量联合成员后TrimT能在编译期完成空白归一化从而在泛型约束中统一用户传入的字符串格式如表单键、路由参数、枚举值字符串配合其他模板字面量工具类型做字符串解析、路径拼接等场景的前置清洗在声明文件中描述去除空白后的输出类型让 IDE 提示直接呈现结果类型。边界与限制TrimT只处理类型层面的字符串不会改变任何运行时行为也不是对trim()的类型声明替代。输入的T必须是字符串字面量类型或字面量联合。若传入宽类型string条件类型无法拆解出精确的infer会退回: S分支原样返回。空白字符集合由Whitespace决定需要覆盖哪些字符完全取决于题目要求可自行扩展。如何验证你的实现本地类型检查仓库采用 pnpm workspace 管理type-challenges/utils作为 workspace 包提供。在仓库根目录完成依赖安装后可直接对单个题目文件做类型检查pnpm install npx tsc --noEmit questions/00108-medium-trim/test-cases.tstest-cases.ts中每个ExpectEqual...都是类型级断言只有当Trim \n\t foo bar \t被推导为foo bar时Equal才返回trueExpect才满足T extends true编译期断言通过反之类型检查将报错。因此测试用例通过即等价于类型推导正确。对照相关题目先做 106・TrimLeft验证单向剥离再做 108・Trim本文题目组合出双向剥离最后做 4803・TrimRight闭环三件套。三者的测试用例分别位于 00106-medium-trimleft/test-cases.ts、00108-medium-trim/test-cases.ts、04803-medium-trim-right/test-cases.ts在空白字符覆盖上保持一致空格、\n、\t可以作为交叉验证。参考答案汇总将上述思路汇总为可直接通过全部用例的完整实现type Whitespace | \n | \t type TrimLeftS extends string S extends ${Whitespace}${infer Rest} ? TrimLeftRest : S type TrimRightS extends string S extends ${infer Rest}${Whitespace} ? TrimRightRest : S type TrimS extends string TrimRightTrimLeftS验证关键用例type a Trim Hello World // Hello World type b Trim \n\t foo bar \t // foo bar type c Trim // type d Trim \n\t // a两侧空格先被TrimLeft剥除再被TrimRight剥除得到Hello WorldbTrimLeft剥除 \n\t 后为foo bar \tTrimRight再剥除\t得到foo barc、d空字符串与纯空白字符串在递归剥离中自然走到终止分支得到。小结TrimT是模板字面量类型 条件递归的经典组合先用Whitespace联合类型建模空白字符集合再分别用${Whitespace}${infer Rest}与${infer Rest}${Whitespace}两个方向的模式匹配实现左、右单向剥离最后串联为双向Trim。通过 test-cases.ts 的 8 个类型断言覆盖无空白、单侧、双侧、混合空白、空串、纯空白六类场景可以完整验证实现的正确性。掌握了这一模式你就具备了用类型系统处理字符串裁剪、格式归一化等场景的核心能力也为后续挑战中更复杂的字符串解析类型做好了铺垫。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐Type Challenges 中等题解用模板字面量类型实现 TrimT 去除字符串两端空白Type Challenges 中等题解用模板字面量类型实现 TrimT 去除字符串两端空白 TrimT 是 Type Challengestype c示例工程type-challenges 第 108 题 Trim 解析用模板字面量类型实现两端空白去除type challenges 第 108 题 Trim 解析用模板字面量类型实现两端空白去除 导读 Trim 是 type challenges 仓库中编号示例工程TypeScript 类型挑战 106用模板字面量类型实现 TrimLeft 去除字符串左侧空白TypeScript 类型挑战 106用模板字面量类型实现 TrimLeft 去除字符串左侧空白 本篇文章围绕 type challenges 仓库中的第 1示例工程上一篇Gutenberg 的 wordpress/deprecated 包从 API 用法到 CHANGELOG 变更史的技术全解下一篇FastF1 v2.1.1 实时时序数据记录与回放Live Timing Data 完整实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考