首页 / 资讯中心 / 文章详情

type-challenges 中等题 10969:用模板字面量类型实现 Integer<T> 整数类型守卫

type-challenges 中等题 10969:用模板字面量类型实现 Integer<T> 整数类型守卫 ★ FEATURED ARTICLE
示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载导读本篇文章围绕 type-challenges 仓库中的第 10969 号中等难度题目展开核心任务是实现一个类型工具IntegerT当传入的number类型是一个整数时原样返回否则返回never。文章会完整还原题目要求与官方测试用例深入拆解利用模板字面量类型template literal types与条件类型实现整数判定的原理并结合仓库内 template.ts、test-cases.ts 等文件给出可直接运行的完整解法与边界用例分析。读完本文你将掌握类型层面的数字判定通用范式并理解as const、字面量类型收窄与泛型数字之间的本质区别。题目概述什么是IntegerT题目出处位于 questions/10969-medium-integer/由 HuaBinghbcraft发起难度标记为medium标签为#template-literal模板字面量类型。官方中文描述只有一句话请完成类型IntegerT类型T继承于number如果T是一个整数则返回它否则返回never。也就是说我们需要编写这样一个类型工具type IntegerT extends number any仓库 template.ts 中给出的是待填空的type IntegerT any你需要将any替换为真正的实现。题目虽短但在类型系统里判断一个数字是否为整数远没有看起来那么简单。TypeScript 的类型层面没有运行时的数值运算能力无法直接调用Number.isInteger因此必须借助字面量类型与模板字面量类型对数字的字符串形态做模式匹配这恰好呼应了它的#template-literal标签。官方测试用例需求的精确边界解题前先完整阅读仓库 test-cases.ts 中的 9 组断言它们严格定义了IntegerT的行为边界import type { Equal, Expect } from type-challenges/utils let x 1 let y 1 as const type cases1 [ ExpectEqualInteger1, 1, ExpectEqualInteger1.1, never, ExpectEqualInteger1.0, 1, ExpectEqualInteger1.000000000, 1, ExpectEqualInteger0.5, never, ExpectEqualInteger28.00, 28, ExpectEqualInteger28.101, never, ExpectEqualIntegertypeof x, never, ExpectEqualIntegertypeof y, 1, ]逐个拆解这些断言可以提炼出三条关键规则纯整数通过Integer1返回1Integer28.00返回28Integer1.0、Integer1.000000000也都返回1。注意1.0、1.000000000、28.00在 TS 字面量层面虽然写法带小数点但它们在类型系统中会被归一化为不带小数的字面量因此它们的小数部分实际上并不存在是合法的整数。真小数被拒Integer1.1、Integer0.5、Integer28.101必须返回never。这里1.1、0.5的小数点是真实存在的正是需要拦截的对象。区分泛型数字与字面量数字let x 1推导出的typeof x是宽泛的number而非1必须返回never而let y 1 as const得到字面量1应当返回1。这要求实现必须拒绝一切非字面量的number类型。前两条规则合起来可以看出一件事判断的钥匙是数字的字面量字符串形态里是否存在小数点。1.0归一化后字符串是1没有小数点1.1的字符串是1.1有小数点。而第三条规则则暗示宽泛的number不能被infer出具体字符串形态自然也会落入never分支。解法一基于字符串形态的.检测既然关键在字符串里有没有小数点最直观的思路就是把T转换成字符串后用模板字面量类型的模式匹配去探测type IntegerT extends number ${T} extends ${infer _Int}.${infer _Dec} ? never : T实现思路用${T}将数字字面量转换为字符串字面量通过extends${infer _Int}.${infer _Dec} 尝试匹配整数部分 小数点 小数部分的结构若能匹配说明存在小数部分返回never否则说明字符串里没有小数点原样返回T。对仓库 test-cases.ts 的用例逐一验证Integer1${1}为1无法匹配x.y结构返回1✅Integer1.1${1.1}为1.1匹配成功返回never✅Integer1.0字面量1.0归一化为1${1.0}为1返回1✅Integer0.5${0.5}为0.5返回never✅Integertypeof xx的类型是number${number}无法被infer具体化条件类型会落入 false 分支返回number本身而非never—— 这与期望不符 ❌也就是说这个标准答案能正确处理所有字面量用例但对Integertypeof x宽泛number会错误地返回number。要让Integertypeof x得到never需要额外的一层保护。解法二拒绝宽泛 number返回 never宽泛的number与字面量数字的区别在于它可以表示任意数值${number}也无法被infer出确定的小数部分。为了把这种不确定性显式映射为never可以在第一层匹配时把能匹配出小数部分和匹配不出任何内容两种情况统一处理type IntegerT extends number ${T} extends ${infer _Int}.${infer _Dec} ? never : T extends number ? T : never这里多加的T extends number ? T : never分支本质上是利用条件类型的分配律当T是联合类型或宽泛类型时条件会按成员逐一展开。对于typeof x number这样的宽泛类型${number}的匹配结果并不稳定配合外层结构可以确保最终落入never。更简洁、也更常见的社区写法是把非整数统一判定为never同时用T extends${number} 先确认T真的是字面量数字而非宽泛numbertype IntegerT extends number number extends T ? never // 宽泛 number非字面量直接拒绝 : ${T} extends ${infer _Int}.${infer _Dec} ? never // 字符串形态含小数点是小数 : T // 否则就是整数原样返回逐条验证官方用例Integer1number extends 1为 false${1}无小数点 →1✅Integer1.1字符串1.1匹配到小数点 →never✅Integer1.0/Integer1.000000000归一化为1字符串1→1✅Integer28.00归一化为28字符串28→28✅Integer0.5/Integer28.101字符串含小数点 →never✅Integertypeof xnumber extends number为 true →never✅Integertypeof ytypeof y是字面量1number extends 1为 false字符串1无小数点 →1✅9 组断言全部通过。原理深挖模板字面量类型如何看见数字这个解法之所以成立依赖 TypeScript 4.1 引入的模板字面量类型template literal types能力。它的核心机制是把字面量类型当作可拆解的字符串支持infer提取与模式匹配type SplitByDotS extends string S extends ${infer Int}.${infer Dec} ? [Int, Dec] : never当输入是1.1时Int被推断为1、Dec被推断为1当输入是1时模式X.Y无法匹配落入never。IntegerT正是复用了这一机制把数字的字符串形态当作判断依据。这里有一个值得注意的 TypeScript 行为数字字面量在类型层面会先归一化再参与${T}转换。所以1.0、1.000000000在类型系统中等同于1转换结果是1天然没有小数点是整数只有真正写成1.1、0.5、28.101这类不可约的十进制小数时字符串里才保留小数点从而被never捕获。这解释了为什么解法不需要截断小数位再比较只需要看字符串里有没有点。从仓库结构看同属#template-literal/#math标签的题目也大量使用这种字符串化思路例如 05140-medium-trunc类型版Math.trunc去掉小数部分、00529-medium-absolute返回正数字符串、02257-medium-minusone数字减一等它们共同构成了类型系统内做数字运算的一整套实践。测试基础设施Equal 与 Expect 从哪来测试用例开头从type-challenges/utils导入了Equal和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 : falseExpectT extends true一个接受true的约束工具任何断言若不为true会直接产生类型错误EqualX, Y通过函数签名比较技巧严格判断两个类型是否完全相同比extends双向兼容更严格能区分any、unknown与具体类型。Integer1返回1之所以能通过ExpectEqualInteger1, 1是因为Equal要求两边类型完全一致而Integer1.1返回never则通过ExpectEqualInteger1.1, never验证。这两组断言一正一反把Integer的语义钉死。关于Equal的用法与陷阱utils/index.d.test.ts 里有更完整的参考用例例如ExpectFalseEqualany, 123表明any与具体类型并不相等——这也是本题目把宽泛number映射为never时需要注意的严格性来源。解法比较与易错点维度解法一仅查小数点解法二拒绝宽泛 number核心写法${T} extends ${infer I}.${infer D} ? never : T先判number extends T再查小数点字面量整数✅✅字面量小数✅✅宽泛number❌ 会返回number✅ 返回never代码量少多一行保护判断实践中容易踩的三个坑漏掉typeof x用例只写查小数点分支会让Integertypeof x返回number无法通过ExpectEqualIntegertypeof x, never。必须显式处理宽泛number。忘记as const的作用let y 1 as const把y的类型收窄为字面量1Integertypeof y才能得到1没有as constlet x 1只会得到number。这正是题目考察的字面量类型 vs 宽泛类型意识。把1.0误判为小数由于 TS 的数字字面量归一化1.0与1等价切勿在解法中假设字符串里出现.就是小数的朴素直觉——28.00同样会被归一化为28。扩展思考科学计数法与大数边界当前官方用例只覆盖了普通十进制写法。如果将来用例扩展还需要考虑科学计数法例如1e21这类数字的字符串形态是1e21不含小数点当前解法会将其判定为整数——这在数值上确实成立1e21是整数行为一致但像1.5e-3这样的写法字符串是0.0015依然会因含小数点而被拒绝。此外当数字非常大或非常小时${T}会采用指数形式输出例如${1e21}为1e21此时若小数部分以指数形式隐藏纯字符串判定可能出现偏差。这些超出官方测试范围的边界情况说明类型层面判整数本质上依赖 TypeScript 对数字字面量的字符串化规则解法的完备性由测试用例集决定。总结IntegerT是 type-challenges 中等难度中非常典型的模板字面量类型应用题官方描述仅一句话真正的考点藏在 test-cases.ts 的 9 组断言里整数原样返回小数返回never1.0、28.00等写法因字面量归一化而等价于整数宽泛的number必须返回never字面量1 as const才能通过。完整解法全部通过官方用例type IntegerT extends number number extends T ? never : ${T} extends ${infer _Int}.${infer _Dec} ? never : T掌握这道题你也就掌握了用字符串形态感知数字这一整套模板字面量技巧可以直接迁移到Trunc、Absolute、MinusOne等同仓库题目中。要亲自验证解法只需在 type-challenges 在线评测中打开 10969 号题Accept the Challenge入口或把 template.ts 与 test-cases.ts 放到本地配合type-challenges/utils运行类型检查即可。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐type-challenges 中等题精解用模板字面量类型实现 CapitalizeTtype challenges 中等题精解用模板字面量类型实现 CapitalizeT 本篇文章基于开源仓库 type challengesCollect示例工程Type Challenges 中等题解用模板字面量类型实现 TrimT 去除字符串两端空白Type Challenges 中等题解用模板字面量类型实现 TrimT 去除字符串两端空白 TrimT 是 Type Challengestype c示例工程type-challenges 中等题 612用模板字面量类型实现 KebabCase 字符串转换type challenges 中等题 612用模板字面量类型实现 KebabCase 字符串转换 本文围绕 type challenges 仓库第 612示例工程上一篇Roc 语言 List.ends_with 用法详解从 REPL 快照测试到源码实现下一篇Agent Governance Toolkit 五语言包功能矩阵全解析Python、TypeScript、.NET、Rust、Go 的治理能力对照与选型指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站