Yuzhe's Blog

yuzhes

531. 字符串转联合类型

531. 字符串转联合类型

题目链接

题目

实现 StringToUnion 类型。接收一个字符串参数,输出该字符串所有字符组成的联合类型。

type Test = "123"
type Result = StringToUnion<Test> // 期望得到 "1" | "2" | "3"

解答

type StringToUnion<S extends string> = 
  S extends `${infer First}${infer Rest}`
    ? First | StringToUnion<Rest>
    : never

深入解析

原理剖析

这个解法利用 TypeScript 的模板字面量类型和条件类型,递归地将字符串分解为单个字符:

  1. 模式匹配S extends \inferFirst{infer First}{infer Rest}` 使用模板字面量推断,提取第一个字符(First)和剩余字符串(Rest`)。

  2. 构建联合类型:每次递归,通过 First | StringToUnion<Rest>First 加入联合类型。

  3. 递归终止:当 S 为空字符串时,模式匹配失败,返回 never。由于 T | never = T,递归干净地结束。

推演示例

StringToUnion<"abc"> 为例:

StringToUnion<"abc">
= "a" | StringToUnion<"bc">
= "a" | "b" | StringToUnion<"c">
= "a" | "b" | "c" | StringToUnion<"">
= "a" | "b" | "c" | never
= "a" | "b" | "c"

为什么 infer 能精确匹配单个字符

当对字符串使用 ${infer First}${infer Rest} 时:

边界情况

type Empty = StringToUnion<"">      // never
type Single = StringToUnion<"x">    // "x"
type Spaces = StringToUnion<"a b">  // "a" | " " | "b"

核心要点

  1. 模板字面量推断${infer X}${infer Y} 是 TypeScript 类型系统中字符串分解的标准模式。

  2. 递归构建联合类型:用 X | RecursiveType<Rest> 递归构建联合类型是处理序列转换的常见套路。

  3. never 的幺元性质:在联合类型中,never 是幺元——加上它不改变结果,非常适合作为递归的终止条件。

  4. 字符级操作:这个模式是很多字符串操作类型的基础,如 SplitJoin 和字符过滤等。