Yuzhe's Blog

yuzhes

字符串分割 Split

字符串分割 Split

题目链接

题目

在类型系统中实现 Split,复制 JavaScript 的 String.prototype.split() 行为。

type result = Split<'Hi! How are you?', ' '>
// 期望结果:['Hi!', 'How', 'are', 'you?']

解答

type Split<
  S extends string,
  SEP extends string
> = string extends S
  ? string[]
  : S extends `${infer Head}${SEP}${infer Tail}`
    ? [Head, ...Split<Tail, SEP>]
    : SEP extends ''
      ? []
      : [S]

核心思路:递归匹配字符串中的分隔符,将分隔符前的部分累积到元组中,并处理空分隔符和泛型 string 的边界情况

逐步拆解

第一步 — 处理泛型 string 类型

string extends S ? string[] : ...

如果 S 是宽泛的 string 类型(非字面量),无法在类型层面进行分割。返回 string[] 以匹配运行时行为。

第二步 — 匹配并在分隔符处拆分

S extends `${infer Head}${SEP}${infer Tail}`
  ? [Head, ...Split<Tail, SEP>]
  : ...

模板字面量推断找到 SSEP 的第一次出现:

Head 放入元组,然后对 Tail 递归。

第三步 — 基础情况

SEP extends '' ? [] : [S]

当不再匹配到分隔符时:

推导过程:

Split<'Hi! How are you?', ' '>

第 1 轮:Head = 'Hi!',Tail = 'How are you?'
  → ['Hi!', ...Split<'How are you?', ' '>]

第 2 轮:Head = 'How',Tail = 'are you?'
  → ['How', ...Split<'are you?', ' '>]

第 3 轮:Head = 'are',Tail = 'you?'
  → ['are', ...Split<'you?', ' '>]

第 4 轮:'you?' 不包含 ' '
  → ['you?']

展开:['Hi!', 'How', 'are', 'you?'] ✓

深入分析

空字符串分隔符

SEP = '' 时,分割应该产生单个字符,就像 'abc'.split('')['a', 'b', 'c']

Split<'abc', ''>

1 轮:'abc' extends `${infer Head}${infer Tail}`
  Head = 'a',Tail = 'bc'
  → ['a', ...Split<'bc', ''>]

2 轮:Head = 'b',Tail = 'c'
  → ['b', ...Split<'c', ''>]

3 轮:Head = 'c',Tail = ''
  → ['c', ...Split<'', ''>]

4 轮:'' extends `${infer Head}${infer Tail}`?否(无法拆分空字符串)
  → SEP extends ''?是 → []

最终:['a', 'b', 'c'] ✓

SEP extends '' ? [] 基础情况专门为此设计——当空分隔符递归到达空字符串时。

多字符分隔符

type T = Split<'a---b---c', '---'>
// ['a', 'b', 'c']

模板字面量匹配将 SEP 作为完整子串处理,而非逐字符匹配。

连续分隔符

type T = Split<'a,,b,,c', ','>
// ['a', '', 'b', '', 'c']

这与 JavaScript 行为一致:连续分隔符产生空字符串。

第 1 轮:Head = 'a',Tail = ',b,,c'
第 2 轮:Head = '',Tail = 'b,,c'
第 3 轮:Head = 'b',Tail = ',c'
第 4 轮:Head = '',Tail = 'c'
第 5 轮:'c' → ['c']
→ ['a', '', 'b', '', 'c'] ✓

为什么用 string extends S 而不是 S extends string

S extends string 总是为真(因为约束)。我们需要检测 S宽泛的 string 类型还是字符串字面量:

string extends 'hello'  // false(string 比 'hello' 更宽)
string extends string   // true

所以 string extends S 只有当 S 本身就是 string(或更宽)时才为真,而不是特定字面量时。

与运行时 split() 的对比

行为运行时类型层
'a,b'.split(',')['a', 'b']['a', 'b']
'abc'.split('')['a', 'b', 'c']['a', 'b', 'c']
'a,,b'.split(',')['a', '', 'b']['a', '', 'b']
'hello'.split(',')['hello']['hello']
''.split(',')['']['']

核心要点