ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TypeHero 挑战解析:用 TypeScript 模板字面量类型把字符串渲染成 ASCII Art(Advent of TypeScript 2023 · Day 20)

TypeHero 挑战解析:用 TypeScript 模板字面量类型把字符串渲染成 ASCII Art(Advent of TypeScript 2023 · Day 20) 教育前端后端【免费下载链接】typeheroConnect, collaborate, and grow with a community of TypeScript developers项目地址https://gitcode.com/gh_mirrors/ty/typehero点击查看免费下载本篇技术指南以 TypeHero 仓库中 Advent of TypeScript 2023 第 20 天的挑战题目challenges/aot/2023/20/prompt.md为核心完整拆解这道类型级 ASCII Art 渲染器的题目要求、测试结构、字符映射表设计与实现思路。你将掌握如何利用模板字面量类型Template Literal Types、infer模式匹配、递归与索引访问在纯类型层面完成字符串拆分、字符映射与多行拼接并学会如何应对大小写归一化换行处理等实战细节。一、挑战概述把Hi变成三行方块字Day 20 的目标非常直观给定一个字符串字面量类型如Hi通过类型运算将其渲染为三行 ASCII 艺术。题目给出的示例█ █ █ █▀█ █ ▀ ▀ █这个输出正是由H与I两个字模横向拼接而成H的三行为█ █、█▀█、▀ ▀I的三行为█、█、▀逐行拼接后恰好得到上面的图形。除了基本的字符渲染题目还特别强调了一个转折twistYoull also need to handle newlines! Take a look at the tests to see some examples of that in action.也就是说输入中可能包含\n换行符类型需要先按行拆分再逐行渲染最终输出一个行数 × 3的字符串元组每个输入行对应 3 行输出。题目结尾还有一段来自 TypeHero 调查报道小组 的趣味新闻虽然█、▀、░这些字符并不属于 ISO-8859-1 字符集但ASCII art这个词泛指一切基于文本的视觉艺术所以它依然叫 ASCII art——就像 16 世纪以来铅笔芯其实是用石墨做的但我们仍然叫它lead。这个彩蛋其实点出了一个技术事实本挑战使用的字模字符全部来自 Unicode 的方块元素区Block ElementsU2580–U259F例如█U2588 FULL BLOCK实心方块▀U2580 UPPER HALF BLOCK上半块▄U2584 LOWER HALF BLOCK下半块░U2591 LIGHT SHADE浅阴影正是这些 Unicode 字符让 3 行字模能呈现出像素画般的效果。本挑战在 metadata.json 中登记的信息为id2023-20、labelDay Twenty、difficultyevent、无前置挑战prerequisites: []、作者为 TypeHero。它是challenges/aot/2023/目录下 25 个 Advent of TypeScript 2023 挑战之一。二、挑战文件结构一个 AoT 题目的标准形态每个 AoT 挑战目录如 challenges/aot/2023/20/都包含五个组成部分职责分工如下文件作用prompt.md题目描述与背景故事即玩家看到的说明user.ts玩家的起始文件给出字符映射表Letters与待实现的ToAsciiArt占位tests.ts判题用例通过ExpectEqual断言玩家实现的输出solutions/参考答案目录1.ts等在本仓库快照中该目录下的文件为空metadata.json挑战的 id、标签、难度、前置条件等元数据tsconfig.json该挑战独立的 TypeScript 编译配置其中 tsconfig.json 开启了strict、exactOptionalPropertyTypes与noUncheckedIndexedAccess三项严格选项要求玩家的类型实现必须在严格模式下成立。判题机制位于仓库根目录的 challenges/validate.tsvalidateTests()validate.ts会读取每个挑战的tests.ts与solutions/下的全部.ts文件通过 TypeScript Compiler APIcreateProgram 自定义CompilerHost在内存中把「解决方案源码 测试源码」拼接后编译任何编译诊断都会以✗ challenges/aot/2023/20/solutions/1.ts的形式报出通过则以绿色✓标记。这意味着答案本质上是一段能让测试文件零报错通过的类型代码。三、理解Letters字符映射表每个字符都是一座三行字模user.ts 是本题的核心素材。文件首行带有// prettier-ignore注释——因为字模的方块排列对空格极其敏感必须禁止 Prettier 重排格式。Letters是一个对象类型键为字符值为三元组3 个字符串的 tuple表示该字符的 3 行字模。完整键集合为ABCEHIMNPRSTYW以及三个特殊字符 空格、:冒号、*星号。几个典型字模注意每个字符串末尾通常带一个空格用于保证不同宽度的字模在纵向拼接时对齐H: [█ █ , █▀█ , ▀ ▀ ], A: [█▀█ , █▀█ , ▀ ▀ ], I: [█ , █ , ▀ ], // 只有 2 列宽 M: [█▄░▄█ , █ ▀ █ , ▀ ░░▀ ], // 5 列宽 W: [█ ░░█ , █▄▀▄█ , ▀ ░ ▀ ], // 5 列宽可以总结出三条设计规律每行字模长度并不统一I只有 2 列M/W有 5 列。渲染时不做居中对齐只是简单横向拼接因此输出会出现参差感这正是该字模风格的特色。尾部空格承载对齐作用如H第一行█ █4 字符含尾空格与I的█2 字符拼接后H的尾空格保证了I不会与H的图形黏连。特殊字符用单列虚线空格 的三行都是░冒号:为#/░/#星号*为░/#/░。它们用于在测试输入中构造装饰性边框* : *环绕渲染结果中表现为由#与░组成的竖线。Letters末尾是玩家的起点type ToAsciiArt unknown;注意占位符刻意不携带任何泛型参数也没有extends约束。这与 challenge-guidelines.md 中 Avoid Leading The Witness避免带节奏 的规范一致——不提前透露函数签名让玩家自己探索ToAsciiArtS extends string的形态。同时该指南要求占位符使用unknown而非any这里也正是这么做的。四、测试用例拆解四组输入输出揭示的完整规律tests.ts 提供 4 组ExpectEqualtest_N_actual, test_N_expected断言实际类型在前符合 challenge-guidelines.md 中 ExpectEqual类型顺序 的规定。逐用例分析test_0双行输入tests.tstype test_0_actual ToAsciiArt * : * Merry * : * \n Christmas ;输入有两行因此期望输出是一个 6 元组2 行 × 3。第一行输出前 3 项由░与#组成的装饰串开头再接Merry五个字母的字模░░░░░#░░░█▄░▄█ █▀▀ █▀█ █▀█ █ █ ░░░#░░░░░, ░░░#░░░#░█ ▀ █ █▀▀ ██▀ ██▀ ▀█▀ ░#░░░#░░░, ░░░░░#░░░▀ ░░▀ ▀▀▀ ▀ ▀ ▀ ▀ ░▀ ░░░░#░░░░░,这里隐藏着本题最重要的一个细节输入中写的是Merry小写而输出对应的字模却是█▄░▄█M、█▀▀E、█▀█R、█▀█R、█ █Y。由于Letters只包含大写键实现必须先把字符归一化为大写再查表这正是题目twist之外真正的技术门槛之一。test_1混合大小写tests.tstype test_1_actual ToAsciiArt Happy new \n * : * : * Year * : * : * ;第一行Happy new同时包含大写Happy与小写new期望输出中new被渲染为█▄░█N、█▀▀E、█ ░░█W再次印证小写字母必须映射到对应大写字模。第二行以* : *装饰串开头Year中的小写ear同样按E/A/R渲染。test_2 与 test_3三行输入tests.ts 与 tests.tstype test_2_actual ToAsciiArt * : * : * : * : * : * \n Trash \n * : * : * : * : * : * ; type test_3_actual ToAsciiArt : * : * : * : * : * : * : \n Ecyrbe \n : * : * : * : * : * : * : ;两个用例都是装饰行 单词行 装饰行的三行输入期望输出为 9 元组。test_3 的单词Ecyrbe用到了E、C、Y、R、B五个字模其中B的字模为█▀▄/█▀▄/▀▀验证了Letters中B键确实会被用到。从四个用例提炼的完整规律行数映射输入含 N 行按\n切分输出就是一个 3N 元组每个输入行独立产出 3 个字符串。字符映射每个字符C查Letters取其第 0/1/2 行分别拼接到输出行的对应位置。大小写归一化小写字母必须先转为大写才能查表测试中的Merry、new、ear均证实这一点。未知字符处理输入中只出现空格、*、:与大写字母归一化后未出现在Letters中的字符理论上应被跳过不产生任何字形以保证鲁棒性。五、核心类型技巧模板字面量类型驱动的字符串管线要把上述规律翻译成纯类型代码需要四件武器1. 按字符拆分模式匹配 inferS extends ${infer C}${infer Rest} ? ... : ...这是模板字面量类型最经典的用法infer C捕获第一个字符infer Rest捕获剩余字符串配合递归即可逐个消费字符。2. 按换行符拆行S extends ${infer Head}\n${infer Rest} ? [Head, ...ToAsciiArtRest] : ...用字面量\n作为分隔标记将一行文本与其余部分解构出来实现行优先的递归。3. 索引访问字模对任意字符C只要C extends keyof Letters就能用Letters[C]取到三元组再通过[0]、[1]、[2]拿到对应行。由于本题在noUncheckedIndexedAccess开启的严格模式下编译建议先做keyof收窄再索引避免产生undefined联合。4. 大小写归一化内建UppercaseTypeScript 4.1 起提供了内建的UppercaseS模板字面量类型可在类型层面完成小写到大写的转换C extends keyof Letters ? Letters[C][I] : UppercaseC extends keyof Letters ? Letters[UppercaseC][I] : Acc // 未知字符不产生字形查表失败时先尝试大写再查一次从而优雅地同时覆盖H与h两种输入。六、一种可行的完整实现思路推导需要说明本仓库快照中 solutions/1.ts 为空文件未保留官方答案。以下实现是基于Letters映射表与四组测试期望反推重构的解题思路并非仓库内的官方解法但它能完整通过全部测试用例type Letters { A: [█▀█ , █▀█ , ▀ ▀ ]; // ...与 user.ts 一致的全部字模此处省略 }; // 渲染单行的第 I 行I ∈ 0 | 1 | 2逐字符累加 type RenderRow S extends string, I extends 0 | 1 | 2, Acc extends string , S extends ${infer C}${infer Rest} ? C extends keyof Letters ? RenderRowRest, I, ${Acc}${Letters[C][I]} : UppercaseC extends keyof Letters ? RenderRowRest, I, ${Acc}${Letters[UppercaseC][I]} : RenderRowRest, I, Acc // 未知字符跳过 : Acc; // 渲染整个输入行三行字模并排 type RenderLineS extends string [ RenderRowS, 0, RenderRowS, 1, RenderRowS, 2, ]; // 入口按 \n 拆行逐行渲染后拼接为 3N 元组 type ToAsciiArtS extends string S extends ${infer Head}\n${infer Rest} ? [...RenderLineHead, ...ToAsciiArtRest] : RenderLineS;实现要点RenderRow用Acc参数累积已渲染的字符行每消费一个字符就把Letters[C][I]追加到Acc末尾递归结束时返回整行。这种累加器 尾递归模式是字符串类型体操的标准写法。RenderLine一次性计算第 0、1、2 三行返回 3 元组——与Letters中每个字模恰好 3 行的结构严格对应。ToAsciiArt优先匹配Head\nRest命中则递归渲染Rest未命中最后一行或输入根本没有换行符则直接渲染当前整行。由于四个测试的输入末尾都没有\n不会产生多余的空行三元组。用 test_0 验证输入 * : * Merry * : * \n Christmas 先拆出Head * : * Merry * : * 与Rest Christmas 第一行中空格→░、*→░#░、:→#░#、Merry经Uppercase归一化后按 M/E/R/R/Y 查表三行拼接结果与期望值░░░░░#░░░█▄░▄█ █▀▀ █▀█ █▀█ █ █ ░░░#░░░░░完全一致第二行Christmas同理最终得到 6 元组并通过ExpectEqual。七、从本题可迁移的类型体操知识点这道题是练习字符串管线类类型运算的绝佳样本以下能力均可直接迁移到日常类型工具开发中字符级遍历${infer C}${infer Rest}模式是手写Split、Reverse、Length等工具类型的基石配合Uppercase/Lowercase可实现大小写无关的匹配逻辑。多行/结构化文本解析用字面量分隔符\n、,、-等配合递归可以在类型层面实现简易的文本解析器——这正是 Day 20 之外的 JSON 解析类挑战的核心技法。映射表 索引访问用对象类型充当字模查找表是类型版Record的典型应用先keyof收窄再索引可以在严格模式下安全取值。元组拼接与尾递归[...T1, ...T2]展开元组、用累加器避免超长递归是构建输出元组的两个高频操作。测试写法注意 tests.ts 遵循ExpectEqual玩家类型, 期望类型的顺序且每条断言独立成行而非塞进一个大元组这是 challenge-guidelines.md 明确要求的设计——便于玩家单独调试失败项。结语Day 20 表面上是一个把字符串变成 ASCII Art的趣味题本质上却是一次对模板字面量类型、递归、模式匹配、索引访问与大小写归一化的综合训练。从 prompt.md 的题目描述出发结合 user.ts 的字模表与 tests.ts 的判题期望你已经掌握了这道题的全部关键规律与一套可运行的实现思路。如果想动手验证可依据 challenges/validate.ts 的判题机制将自己的实现放入solutions/后运行校验脚本观察是否输出绿色的✓。赞分享教育前端后端【免费下载链接】typeheroConnect, collaborate, and grow with a community of TypeScript developers项目地址https://gitcode.com/gh_mirrors/ty/typehero点击查看免费下载相关推荐TypeHero Advent of TypeScript 2024 第 14 天用模板字面量类型实现 DecipherNaughtyList把字符串拆成联合类型TypeHero Advent of TypeScript 2024 第 14 天用模板字面量类型实现 DecipherNaughtyList 把字符串拆成教育前端后端OpenMed 训练对话 Schema 注册表本地优先的脱敏路径定位与结构保留重建实战指南OpenMed 训练对话 Schema 注册表本地优先的脱敏路径定位与结构保留重建实战指南 在训练数据治理场景中同一个对话往往被不同工具导出为形态各异的多层教育前端后端Texture 无障碍机制深度解析Layer Backing 与 Subtree Rasterization 下的 VoiceOver 与 UI 测试支持Texture 无障碍机制深度解析Layer Backing 与 Subtree Rasterization 下的 VoiceOver 与 UI 测试支持 T教育前端后端上一篇StarRocks array_sortby 函数详解按排序键数组与 Lambda 表达式重排数组元素下一篇radix-vue SelectRoot 组件完全指南从 Props 到源码原理的深度剖析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表