- 示例工程
【免费下载链接】type-challenges
Collection of TypeScript type challenges with online judge
本文围绕开源仓库 type-challenges 的日文版 README(README.ja.md)展开,系统讲解这个「TypeScript 型パズル集」的项目定位、五级难度挑战矩阵、标签分类体系、在线判题原理与贡献方式。读完本文,你将清楚如何按难度挑题、理解每一道题的验证机制(template + test-cases + 类型工具库),并掌握参与这个社区型项目贡献的具体路径。
项目定位:一本可以「玩」的 TypeScript 类型系统教科书
type-challenges 是一个 TypeScript 类型挑战合集(Collection of TypeScript type challenges with online judge)。它在 README 开头用一句略带调侃的删除线自述定位:「凭借 TypeScript 众所周知的(图灵完备)类型系统的力量」。这句话虽然被划掉,却准确点出了项目的灵魂:把类型系统本身当作一种可以编程、可以练习、甚至可以实现计算的语言来对待。
与普通编程题库不同,这里的每一道题都不是让你写业务逻辑,而是让你亲手实现一个类型。正如 README 所言:
- 高质量的类型有助于规避潜在 Bug(potential bugs),同时提升项目的可维护性;
- 这个项目旨在帮助你理解类型系统到底如何运转、学会自己编写 Utility 类型,或者纯粹享受解类型谜题的乐趣;
- 项目还致力于形成一个社区:你可以把现实世界真实遇到的问题抛出来,答案与解法可能成为新的挑战题目。
也就是说,你在这里练的不是语法糖,而是类型编程的思维方式——这恰恰是日常工程中「类型写得好不好」的分水岭。
为什么值得练:高质量类型与潜在 Bug 的关系
README 的核心论点很朴素但极其实用:高品質な型は潜在的なバグを回避しつつ、プロジェクトの保守性を向上させるのに役立ちます(高质量的类型有助于规避潜在 Bug、提升项目可维护性)。
这句话在实际工程中对应三类真实收益:
- 把错误挡在编译期:例如
Pick<T, K>这类类型工具一旦写错,调用方的对象结构就会在编译阶段报错,而不是等到运行时才发现字段缺失。 - 让重构更安全:类型越精确,IDE 提示越准确,改动一个接口后所有使用点会被编译器自动「追查」出来。
- 形成可复用的类型资产:通过挑战亲手实现的
MyPick、MyReadonly等类型,可以直接沉淀到自己的工具库中。
README 还提示:你可以借助已有的类型工具库,例如 type-fest;也有一些目前不再积极维护、但值得参考的老牌包,如utility-types、ts-toolbelt、SimplyTyped。做题时对照这些库的实现,是提升类型功力的捷径。
挑战体系:五级难度矩阵
README 将全部挑战按难度分为五档,并以不同颜色的徽章(shields.io badge)标识,点击徽章即可跳转到对应题目的日文版详情页。难度与规模分布如下:
| 难度 | 日语名称 | 数量 | 徽章颜色 | 代表题目 |
|---|---|---|---|---|
| お試し(热身) | warm-up | 1 | teal | 13・Hello World |
| 初級(初级) | easy | 13 | 绿(7aad0c) | 4・Pick、7・Readonly、14・First of Array、43・Exclude、189・Awaited、533・Concat 等 |
| 中級(中级) | medium | 104 | 黄(d9901a) | 2・Get Return Type、3・Omit、9・Deep Readonly、296・Permutation、459・Flatten、612・KebabCase 等 |
| 上級(高级) | hard | 55 | 红(de3d37) | 6・Simple Vue、17・Currying 1、55・Union to Intersection、14080・FizzBuzz、31797・Sudoku 等 |
| 最上級(极限) | extreme | 17 | 紫(b11b8d) | 5・Get Readonly Keys、216・Slice、274・Integers Comparator、476・Sum、517・Multiply、6228・JSON Parser 等 |
从数量分布可以看出选题节奏:初级的 13 题覆盖最常用的内置类型工具(Pick、Readonly、Exclude、Awaited、Parameters、Concat、Push、Unshift、Includes 等),是「重建标准库」的入门训练;中级的 104 题是题库的绝对主体,涵盖字符串模板字面量、联合类型分发、递归、数学运算(Fibonacci、Greater Than、Square)等高频考点;上级的 55 题开始出现框架级模拟(Simple Vue、Pinia)、算法类型实现(Two Sum、FizzBuzz、Sudoku);最上級的 17 题则是纯粹的「类型体操」极限测试,例如在类型层面实现 JSON 解析器、整数比较器、乘法与减法。
标签分类体系:按知识点精准挑题
除了难度,README 还提供了「タグ別」(按标签)分类视图,把每道题挂到具体的技术知识点下。这是按需补课的最佳入口。标签体系包括:
- #JSON:如 26401・JSON Schema to TypeScript、6228・JSON Parser
- #array:数量最多的一类,覆盖 First、Last、Pop、Shift、Flatten、Chunk、IndexOf、Unique、Transpose 等数组算法型题目
- #string / #template-literal:Trim、Replace、Capitalize、CamelCase、SnakeCase、KebabCase、Split、StartsWith/EndsWith 等字符串模板字面量技巧
- #union / #infer:Pick、Exclude、Omit、Tuple to Union、Union to Intersection、Union to Tuple 等联合类型与条件推断核心考点
- #object / #object-keys:Readonly、Deep Readonly、Merge、Diff、PickByType、PartialByKeys、ObjectEntries、ObjectKeyPaths 等对象操作
- #math / #number:Absolute、MinusOne、Fibonacci Sequence、Square、Binary to Decimal、IsNegativeNumber 等把类型系统当计算器用的题目
- #built-in:重新实现 TypeScript 内置 Utility 类型的题目(Pick、Readonly、Exclude、Awaited、Parameters、ReturnType、Omit)
- #recursion:Permutations of Tuple、Compare Array Length、Camelize、Binary Addition 等递归型题目
- #promise:Awaited、Promise.all、Unbox
- #tuple:Tuple Length、Tuple to Union、Zip、Chunk、Reverse、Construct Tuple 等
- #application / #vue / #this:Chainable Options、Simple Vue、Vue Basic Props、Pinia 等贴近真实框架的类型题
- #game:Sudoku、Valid Sudoku 等游戏类挑战
- 以及#conditional type、#intersection、#map、#filter、#optional、#parameters、#rest、#split、#variadic、#utils、#deep、#readonly、#cif等细分类目
从源码结构看,每个标签都对应着 README 中由脚本自动生成的徽章链接(<!--challenges-start-->与<!--challenges-end-->注释之间的内容),这意味着题目清单是机器维护的:新增题目后只需运行 scripts/readme.ts 即可重新生成全部难度/标签清单。
在线判题机制:你的答案是如何被验证的
这是 type-challenges 与普通题库最不同的地方:它用类型系统本身来做断言(README 在 Hello World 一题中明确写道:「Type Challenges では、型システム自体を使用してアサーションを実行します」)。也就是说,没有运行时测试,判题靠的是 TypeScript 编译器的类型检查。
每道题在仓库中都由两个文件组成,以第 4 题 Pick 为例:
- 模板文件 questions/00004-easy-pick/template.ts:
type MyPick<T, K> = any你只需要把any替换成真正的实现。
- 测试文件 questions/00004-easy-pick/test-cases.ts:
import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Expected1, MyPick<Todo, 'title'>>>, Expect<Equal<Expected2, MyPick<Todo, 'title' | 'completed'>>>, // @ts-expect-error MyPick<Todo, 'title' | 'completed' | 'invalid'>, ]测试用例的语义非常直观:
Expect<Equal<A, B>>:断言MyPick<Todo, 'title'>与预期的Expected1类型完全相等;// @ts-expect-error注释:断言传入不存在的键'invalid'时必须产生类型错误——如果你的MyPick实现没有拒绝非法键,这一行就会反过来报错。
而Equal与Expect等断言工具的真实实现位于仓库的 utils/index.d.ts:
export type Expect<T extends true> = T export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false export type NotAny<T> = true extends IsAny<T> ? false : trueEqual通过「把类型放入相同的函数签名再进行条件类型比较」的技巧,能区分出any、never、联合类型等易混情况;NotAny则用于确保答案没有偷懒地停留在any。热身题 00013-warm-hello-world 的测试用例就是Expect<NotAny<HelloWorld>>与Expect<Equal<HelloWorld, string>>两条断言,分别检查「不是 any」和「恰好等于 string」。可见只要你的type HelloWorld = any模板不改掉,判题就永远无法通过。
从仓库结构可以推断,@type-challenges/utils是经由 pnpm-workspace.yaml 组织的 workspace 包(在 package.json 中声明为"@type-challenges/utils": "workspace:*"),被所有test-cases.ts统一引用,构成了整个题库的判题基础设施。
推荐资料:官方文档与学习资源
README 为学习者整理了官方与日语两方面的参考资料:
公式(官方,英语)
- The TypeScript Handbook,并重点推荐其中四节:
- Unions and Intersection Types(联合与交叉类型)
- Literal Types(字面量类型)
- Utility Types(内置工具类型)
- Advanced Types(高级类型)
- The New Handbook(官方新手册仓库)
その他(日语)
- TypeScript Deep Dive – TypeScript 的型系统
建议的搭配用法:先读官方 Handbook 的 Utility Types 与 Advanced Types 建立概念,再在题库的#built-in标签下亲手重造 Pick/Readonly/Omit 加深理解,遇到infer、递归卡壳时回到文档查证。
如何参与贡献:让真实问题变成新挑战
README 专门开辟了「コントリビュート方法」一节,列出了六种贡献方式:
- 分享自己的解答(Share your answers / solutions)
- 提议新的挑战题目(Propose new challenges)
- 为已有挑战补充测试用例(Add more test cases to the existing challenges)
- 提供解题思路相关的学习资料或想法(Provide learning resources or ideas of how to solve challenges)
- 分享在真实项目中遇到的问题——无论是否已有解法,社区都会帮忙(Share the problems you have faced in real-world projects)
- 参与 Issue 讨论、帮助他人,或为项目基础设施做贡献,相关事项见 TODOs.md
贡献流程很简单:直接打开一个 Issue 并选择对应的模板即可。其中第 5 点尤其值得注意——它是这个项目「从社区中来、到挑战中去」的闭环:你在业务中踩过的类型坑,很可能就是下一道新题目的素材。
从源码看,题目本身的工程化程度也很高:每道题目录下除了template.ts与test-cases.ts,还有元数据文件 info.yml(记录标题、作者、难度)以及多语言 README(README.ja.md、README.zh-CN.md、README.ko.md、README.pt-BR.md等),scripts/loader.ts 会统一加载这些信息,scripts/actions 目录则承载了与题目相关的自动化脚本(包括 Issue/PR 处理),可见整套题库是可以被脚本批量维护和自动生成文档的。
项目背景与许可
README 的致谢部分说明了项目的起源:它诞生于作者与 @hardfist、@MeCKodo 一起在实际业务中解决类型问题的过程,并感谢 @sinoon 在项目早期贡献了大量反馈;日语社区的翻译与维护则由 @kawamataryo 负责。项目以MIT协议开源,你可以放心地在本地学习、修改并复用其中的题目与解法。
结语:从挑一道题开始
回到 README 的核心信息:五级难度 + 标签分类,构成了一个可以「按图索骥」的类型学习路径——想热身就从 13・Hello World 开始,想重建标准库就刷#built-in标签,想挑战类型系统极限就直奔最上級。每一道题背后都有template.ts的起点和test-cases.ts的类型级判题器在等你,而社区则随时欢迎你把真实世界的类型难题变成下一道挑战。
- 示例工程
【免费下载链接】type-challenges
Collection of TypeScript type challenges with online judge
相关推荐
攻克Type Challenges:从Includes类型挑战掌握TypeScript高级类型
攻克Type Challenges:从Includes类型挑战掌握TypeScript高级类型 TypeScript已成为现代前端开发的必备技能,而泛型编程则是
示例工程Type Challenges(type-challenges):在 TypeScript 类型系统上刷题通关的完整指南
Type Challenges(type challenges):在 TypeScript 类型系统上刷题通关的完整指南 Type Challenges 是一个
示例工程Ansible Community General Collection 插件体系解析:从回调到连接的完整指南
Ansible Community General Collection 插件体系解析:从回调到连接的完整指南 想要掌握 Ansible 自动化运维的精髓吗??
运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考