
示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载本篇技术指南以 type-challenges 仓库中的 BEM style string 题目 为核心系统讲解如何使用模板字面量类型、元组索引访问与联合类型分发实现一个能在类型层面自动生成 BEM 类名的BEMB, E, M工具类型。读完本文你将掌握模板字面量类型Template Literal Types在多个联合占位符下的自动展开机制理解空元组条件分支的处理技巧并能独立通过该题的全部测试用例。题目背景什么是 BEM 命名规范BEMBlock、Element、Modifier是 CSS 类命名中非常流行的一种方法论。原题描述README.md给出了它的核心约定Block块独立可复用的组件单元例如btnElement元素依赖某个 Block 的组成部分用__连接例如btn__priceModifier修饰符改变 Block 或 Element 样式的变体用--连接例如btn--big、btn__price--warning。也就是说类名的组合规则是Block、Block__Element、Block--Modifier与Block__Element--Modifier四种形态。本题的目的就是把这套命名规则编译进 TypeScript 的类型系统只要给出 Block 名字、Element 列表和 Modifier 列表类型系统就能自动推导出所有合法类名的联合类型。题目来自作者 Songhngithub 账号 songhn233难度为 medium标签为template-literal、union、tuple详见 info.yml。题目要求与函数签名题目要求实现type BEMB extends string, E extends string[], M extends string[] any签名在 template.ts 中给出其中B是一个字符串字面量类型代表 Block 名E是一个字符串数组元组代表 Element 列表可以为空M是一个字符串数组元组代表 Modifier 列表可以为空。期望的输出是由这三个参数生成的字符串联合类型。例如BEMbtn, [price], []应推导为btn__priceBEMbtn, [price], [warning, success]应推导为btn__price--warning | btn__price--successBEMbtn, [], [small, medium, large]应推导为btn--small | btn--medium | btn--large。注意E与M的约束是string[]但测试中传入的都是具名元组如[price]我们需要依赖元组的每个元素类型可枚举这一特性来完成遍历。三个关键的类型级工具在动手之前先梳理实现所需的三个 TypeScript 类型能力它们恰好也是本题的三个官方标签。1. 模板字面量类型template-literal模板字面量类型允许把字符串字面量与占位符拼接成新类型type Greet hello ${world | ts} // 结果是 hello world | hello ts关键性质是分发distribution当模板串中存在联合类型占位符时TypeScript 会自动对联合的每个成员各生成一个结果并合并为新的联合。如果有多个占位符都是联合类型则会生成它们的笛卡尔积全部组合。这正是我们一键生成所有 BEM 类名组合的底层机制。2. 元组索引访问tuple对元组使用数字索引访问可以得到元素类型而T[number]则会取到所有元素类型的联合。例如type E [warning, success] type EUnion E[number] // warning | success这一步将元组列表转化为联合集合再交给模板字面量类型去分发。3. 条件类型判断空元组conditional type通过T extends [] ? A : B可以判断一个元组是否恰好为空type EmptyT extends string[] T extends [] ? true : false type A Empty[] // true type B Empty[x] // false空数组时我们不希望拼出多余的__或--前缀因此每个占位符都需要这个分支。从测试用例反推期望行为仓库为本题提供了三组测试用例test-cases.ts全部通过ExpectEqual...断言其中Expect与Equal来自仓库的 utils/index.d.tsimport type { Equal, Expect } from type-challenges/utils type cases [ ExpectEqualBEMbtn, [price], [], btn__price, ExpectEqualBEMbtn, [price], [warning, success], btn__price--warning | btn__price--success , ExpectEqualBEMbtn, [], [small, medium, large], btn--small | btn--medium | btn--large , ]三个用例覆盖了三种典型场景也即我们实现的验收标准用例BEM期望结果1btn[price][]btn__price2btn[price][warning, success]btn__price--warning \| btn__price--success3btn[][small, medium, large]btn--small \| btn--medium \| btn--large注意用例 1 中M为空期望结果没有--后缀用例 3 中E为空期望结果没有__部分——这提醒我们空数组分支必须被正确处理。解法一单类型 条件分支最简实现把上面三个能力组合起来最直接的写法如下type BEMB extends string, E extends string[], M extends string[] ${B}${E extends [] ? : __${E[number]}}${M extends [] ? : --${M[number]}}逐段拆解${B}固定拼接 Block 名${E extends [] ? :__${E[number]}}若E为空则拼接空串否则拼接__加元素联合如__price${M extends [] ? :--${M[number]}}若M为空则拼接空串否则拼接--加修饰符联合。由于${E[number]}与${M[number]}都是联合类型占位符模板字面量类型会自动求笛卡尔积。用例 2 中E[number]是priceM[number]是warning | success于是两个分支组合出btn__price--warning与btn__price--success两个成员用例 1 中M分支为空串不产生额外组合。从语义上看E extends []判断的是该元组恰好为空元组。测试用例传入的都是字面量元组因此这个判断是精确的如果传入的是宽泛的string[]条件会走向 false 分支但题目约束要求传入的是字符串数组测试中均为具名元组所以这不是问题。解法二拆分辅助类型更清晰的结构如果希望代码更易读、也便于单元测试每个片段可以把 Element 与 Modifier 的拼接拆成两个辅助类型type WithElementE extends string[] E extends [] ? : __${E[number]} type WithModifierM extends string[] M extends [] ? : --${M[number]} type BEMB extends string, E extends string[], M extends string[] ${B}${WithElementE}${WithModifierM}两种写法在结果上完全等价区别只是代码组织方式。拆分后WithElement[price, icon]可单独推导为__price | __icon方便逐段验证逻辑。边界情况与进阶思考当 E 和 M 同时为空此时两个分支都取空串结果为${B}即仅有 Block 名btn。这在语义上是合理的一个只有 Block、没有 Element 和 Modifier 的类名就是 Block 本身。原测试虽未覆盖此场景但从实现逻辑看行为是确定的。多个 Element 与多个 Modifier 的组合如果E与M都有多个元素例如BEMcard, [title, body], [active, hover]模板字面量类型会生成 2 × 2 4 个成员card__title--active | card__title--hover | card__body--active | card__body--hover。这正是联合占位符分发笛卡尔积特性的体现也符合 BEM 的语义——每个元素与每个修饰符的组合都是合法类名。为什么是E[number]而非遍历熟悉TupleToUnion题00010-medium-tuple-to-union的读者会发现本题不需要像TupleToUnion那样用递归/infer去逐个剥取元组元素因为模板字面量类型可以直接消费E[number]产生的联合。这种索引访问代替遍历的技巧在TrimLeft、Join等同类 template-literal 题目如 00106-medium-trimleft、05310-medium-join中同样常见是一条值得掌握的通用思路。泛型约束的意义签名中B extends string保证了B一定能放进模板字面量E extends string[]、M extends string[]保证了E[number]、M[number]是string子类型可以直接拼接。如果去掉这些约束类型检查器会因无法确认占位符类型而报错——这也是 type-challenges 题目模板普遍采用严格泛型约束的原因。验证与运行环境说明本仓库的题目遵循 README.md 中的说明所有挑战都在 TypeScript strict 模式 也开启了strict: true。你可以在本地完成验证pnpm install后运行pnpm generate即可在生成的 playground 目录中编辑并检验解答也可以在支持 TypeScript 的 IDE 中直接打开 template.ts 与 test-cases.ts让类型检查器替你运行测试断言。测试断言依赖Equal对两个类型做严格的结构比较定义见 utils/index.d.ts因此任何多余的联合成员或缺失的分支都会导致用例失败反馈非常精确。总结BEMB, E, M是一道将字符串生成规则翻译为类型计算的经典 medium 题。回顾核心步骤用条件类型E extends [] ? : ...处理空元组避免多余分隔符用元组索引访问E[number]/M[number]把列表展开为联合类型用模板字面量类型${B}${__...}${--...}自动完成联合分发与笛卡尔积组合。整个实现不到一行核心表达式却完整覆盖了 template-literal、union、tuple 三个类型系统主题并为在类型层面描述领域命名规则提供了简洁的范式——同理可用于路由路径、样式令牌、组件 props 枚举等场景。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐N_m3u8DL-RE 流媒体下载教程DASH/HLS/M3U8 下载与直播录制的完整指南N_m3u8DL RE 流媒体下载教程DASH/HLS/M3U8 下载与直播录制的完整指南 想保存在线视频网站却只给播放不给下载手里只剩一串 m3u8 或CLI音视频攻克BEM命名难题Type-Challenges中的高级类型实战指南攻克BEM命名难题Type Challenges中的高级类型实战指南 你是否曾在CSS命名中遇到过样式冲突是否为复杂组件的类名结构感到困惑BEMBloc示例工程终极指南Material Design Lite样式系统的BEM命名规范解析终极指南Material Design Lite样式系统的BEM命名规范解析 Material Design LiteMDL是一个轻量级的前端框架它通过前端UI组件上一篇Bokeh 实时音频频谱图实战基于 Bokeh Server、流式更新与自定义扩展构建瀑布图应用下一篇FastLED 测试与编译命令实战指南Bash 包装脚本、挂起检测与多构建模式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考