news 2026/10/1 1:56:48

type-challenges 题解 00529:用模板字面量类型实现 `Absolute` 绝对值类型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
type-challenges 题解 00529:用模板字面量类型实现 `Absolute` 绝对值类型
  • 示例工程

【免费下载链接】type-challenges

Collection of TypeScript type challenges with online judge

项目地址:https://gitcode.com/GitHub_Trending/ty/type-challenges
点击查看免费下载

本篇文章围绕 type-challenges 仓库中的中等难度题目 00529「Absolute」展开,详细解析其题目要求、测试用例与标准实现方案。题目要求实现一个接收string | number | bigint的类型工具Absolute,将输入转换为"正数字符串"。读完本文,你将掌握模板字面量类型(template literal types)配合infer的模式匹配技巧,理解数字、字符串与 bigint 三类输入在类型层统一处理的方法,并学会从仓库测试用例反推类型行为边界。

题目背景:仓库中的 00529「Absolute」

本题目录为 questions/00529-medium-absolute,是 type-challenges 精选类型挑战中的一道中等难度题。从 info.yml 可以看到它的元信息:

  • 难度:medium
  • 标签:math, template-literal(数学运算 + 模板字面量)
  • 作者:Andrey Krasovsky(GitHub:bre30kra69cs)

题目同时提供 英文版 README、简体中文版、日文版 与 韩文版,题目描述一致。

题目要求

以日文版 README 为例,原题表述为:"string、number 或 bigint を受け取り、正の数を出力するAbsolute型を実装します"(实现一个接收 string、number 或 bigint,并输出正数的Absolute类型)。英文版更精确地补充了输出形式:"The output should be a positive number string"——输出必须是一个正数字符串。

原文档给出的示例:

type Test = -100; type Result = Absolute<Test>; // expected to be "100"

注意三个关键点:

  1. 输入类型:number、string、bigint三种均可;
  2. 输出类型:不是数字类型100,而是字符串字面量类型"100";
  3. 绝对值语义:负数要去掉负号,正数保持不变。

template.ts 给出了起始模板,类型参数约束已就位,但实现体为any,等待填答:

type Absolute<T extends number | string | bigint> = any

测试用例驱动的需求全景

题目仓库中真正决定"什么算通过"的是 test-cases.ts。它通过Expect<Equal<...>>断言逐一校验实现结果,完整覆盖了三类输入与各种边界情况:

import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Absolute<0>, '0'>>, Expect<Equal<Absolute<-0>, '0'>>, Expect<Equal<Absolute<10>, '10'>>, Expect<Equal<Absolute<-5>, '5'>>, Expect<Equal<Absolute<'0'>, '0'>>, Expect<Equal<Absolute<'-0'>, '0'>>, Expect<Equal<Absolute<'10'>, '10'>>, Expect<Equal<Absolute<'-5'>, '5'>>, Expect<Equal<Absolute<-1_000_000n>, '1000000'>>, Expect<Equal<Absolute<9_999n>, '9999'>>, ]

把这 10 个用例整理成表,可以清晰看到必须覆盖的行为:

输入类型用例期望输出行为要点
number0'0'正数原样转为字符串
number-0'0'数字 -0 在类型层与 0 等价
number10'10'正数原样
number-5'5'负数去除负号
string'0''0'字符串正数原样
string'-0''0'字符串负号需剥离
string'10''10'字符串正数原样
string'-5''5'字符串负数剥离负号
bigint-1_000_000n'1000000'负数 + 数字分隔符展开
bigint9_999n'9999'正数 + 分隔符展开

值得注意的边界事实有两点:

  • -0与'-0'结果相同但成因不同:TypeScript 在类型层面不存在独立的-0字面量类型,-0与0等同,直接转字符串就是'0';而字符串字面量'-0'保留了负号,需要靠模式匹配剥离,结果同样是'0'。
  • bigint 的数字分隔符会被展开:输入中写的1_000_000n(数字分隔符是合法 TS 语法)在转换后得到的是'1000000'而非'1_000_000',说明类型系统在将 bigint 字面量转为模板字面量时执行了与运行时String(bigint)一致的十进制规范化。

断言工具Equal与Expect来自仓库的 utils/index.d.ts,其中Equal<X, Y>采用经典的函数逆变比较法精确判断两个类型是否完全一致:

export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false export type Expect<T extends true> = T

这意味着实现结果的字面量形式必须与期望完全一致(例如'5'而非number),这也解释了为什么所有输出都是精确的字符串字面量类型。

标准解法:模板字面量 + infer 模式匹配

利用模板字面量类型,实现只需三行:

type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer U}` ? U : `${T}`

逐层拆解

第一步:统一输入形态。`${T}`是模板字面量类型,它能把number、bigint字面量以及string字面量统一转换为字符串字面量类型:

  • Absolute<10>中${10}得到'10';
  • Absolute<-5>中${-5}得到'-5';
  • Absolute<-1_000_000n>中${-1_000_000n}得到'-1000000';
  • 字符串输入则原样保留,Absolute<'-5'>中${'-5'}仍是'-5'。

第二步:模式匹配剥离负号。用`-${infer U}`对转换后的字符串做模式匹配:若字符串以-开头,则用infer U捕获负号之后的部分并直接返回;若不匹配(没有负号),则退回返回${T}。

这里infer在条件类型中承担"类型层面的解构赋值":它从模板字面量的结构中推断出剩余部分U,这正是解决本类字符串处理问题的核心手法。

对照测试用例验证

  • Absolute<0>→${0}为'0',不匹配`-${infer U}`,返回'0'✓
  • Absolute<-0>→ 类型层与0等价,同上返回'0'✓
  • Absolute<-5>→'-5'匹配,U = '5',返回'5'✓
  • Absolute<'-5'>→ 字符串'-5'匹配,U = '5',返回'5'✓
  • Absolute<'-0'>→ 匹配-${infer U},U = '0',返回'0'✓
  • Absolute<-1_000_000n>→${-1_000_000n}规范化为'-1000000',匹配后返回'1000000'✓
  • Absolute<9_999n>→${9_999n}规范化为'9999',不匹配负号分支,返回'9999'✓

全部 10 个用例均可通过。

解法变体

将负号匹配直接放在输入上也是可行的,因为string是约束的一部分,负号只可能出现在字符串或转换为字符串后的数字中:

type Absolute<T extends number | string | bigint> = T extends `-${infer U}` ? U : `${T}`

但要注意此写法中T若为number/bigint,先要能参与模板匹配(TS 会将其视为字面量参与匹配),语义上与标准写法等价;显式写`${T}`的做法则更清晰地传达"先统一转字符串再匹配"的意图,也更容易让阅读者理解。

此外,既然目标是"剥掉一个前导负号",本解法不依赖递归即可完成——这符合题目的 medium 定位;如果输入被定义为任意深度嵌套的字符串则才需要考虑递归展开,本题目不需要。

深入理解:模板字面量类型的转换规则

这道题之所以归入template-literal标签,是因为它的核心引擎是模板字面量类型。可以从 guides/infer.md 与 guides/recursive.md 等仓库指南进一步延伸学习。

结合本题目可以沉淀以下类型行为事实(均可由 test-cases.ts 的断言反推确认):

  1. number 字面量转字符串:`${5}`得到'5',负号保留(`${-5}`得到'-5'),且-0与0在类型层无区分,统一得到'0'。
  2. bigint 字面量转字符串:等价于十进制表示的规范化字符串,源码中的数字分隔符(underscore)被展开移除。
  3. string 字面量保持原样:`${'-0'}`仍是'-0',字符串中负号的信息得以保留,因此必须依赖匹配剥离。
  4. 模板字面量 +infer可做"前缀剥除":T extends `-${infer R}` ? R : ...是字符串前缀匹配的标准范式,与TrimLeft、Replace等题目(如仓库中的 00106-medium-trimleft)共享同一套方法论。

如何在本地验证你的实现

type-challenges 仓库采用 pnpm workspace 组织,测试基于 TypeScript 编译器断言完成。你可以按以下流程在本地运行本题的测试:

git clone https://gitcode.com/GitHub_Trending/ty/type-challenges cd type-challenges pnpm install

随后编辑 questions/00529-medium-absolute/template.ts,将any替换为你的实现,再用 TypeScript 编译器检查 test-cases.ts 是否无类型错误——当所有Expect<Equal<...>>断言成立时,编译即通过。仓库的 package.json 中还提供了pnpm lint等脚本辅助校验,测试工具类型Equal/Expect定义在 utils/index.d.ts 中,可随时查阅。

小结

Absolute是模板字面量类型入门到进阶的一座里程碑:它同时考验类型参数约束、字面量统一转换、infer模式匹配与边界情况意识。标准答案`${T}` extends `-${infer U}` ? U : `${T}`只有短短一行,却完整覆盖了 number、string、bigint 三类输入,以及-0、数字分隔符等容易被忽视的边界。对照 test-cases.ts 的 10 条断言逐项验证,你就能确认自己的实现真正符合题目要求——这套"先读用例、再写类型、最后逐条验证"的方法论,同样适用于仓库中其余 600 余道类型挑战。

  • 示例工程

【免费下载链接】type-challenges

Collection of TypeScript type challenges with online judge

项目地址:https://gitcode.com/GitHub_Trending/ty/type-challenges
点击查看免费下载
上一篇:突破认知边界:MMLU跑分背后的模型训练性能革命与实战指南
下一篇:AI Dev Tasks入门教程:从零开始掌握AI辅助开发的结构化工作流

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 1:56:19

游戏引擎架构设计:从团队分工到模块边界与内存管理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:55:56

keil软件调试debug printf viewer 查看printf输出没有内容,谁有解决办法

网上说的添加一段重新定义输出的代码&#xff0c;然后配置一下debug那种方法我试过了 然后还有一种是配置这里也试过了还是不行&#xff0c;查来查去就只有这两种方法 而且上面这两种方法在运行时都会卡在 while (ITM_Port32(0) 0); 这行 然后我自己查一下ai&#xff0c;改了源…

作者头像 李华
网站建设 2026/10/1 1:55:31

时间序列建模必知:严平稳与宽平稳的区别与应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华