Skip to content
webtype.org#217 split-string · par 4

No. 217 · July 25, 2026 · Hard

Split

Implement `Split<S, D>` so it splits the string `S` on every occurrence of the delimiter `D`, returning a tuple of the pieces. A string with no delimiter yields a one-element tuple.

01

Try the puzzle yourself

Par 4

Puzzle

split-string.ts
Stroke 1 of 4Not run yet

Replace ??? — your solution is checked against the cases below.

Checks

4
  • Split<'a,b,c', ','>
    ['a', 'b', 'c']
  • Split<'nope', ','>
    ['nope']
  • Split<'a/b', '/'>
    ['a', 'b']
  • Split<'', ','>
    ['']

How a check is judged Exact type equality, not assignability — an intersection is not the same as the flattened object.

How everyone did

Nobody is counting yet. Score distribution and the short game board arrive with accounts — until then your results stay on this device.

Archive
02

Annotated solution

Published July 26, 2026

The solution

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

The common wrong answer

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

Without the spread, each recursive result is nested one level deeper: `["a", ["b", ["c"]]]`. The `...` is what flattens the recursion into a single tuple.

Line by line

  1. `${infer Head}${D}${infer Tail}`

    Inference against a template literal is greedy from the left, so `Head` binds the shortest prefix that lets the rest of the pattern match — giving you the first field, not the last.

  2. [Head, ...Split<Tail, D>]

    The spread inlines the recursive tuple, so each level contributes one element to a flat result.

  3. : [S]

    No delimiter left. The whole remaining string is the final piece, wrapped in a tuple so the spread above has something to inline.

Takeaway

Recursive string types decompose head-first and rebuild with spreads. Forgetting the spread is the single most common bug in type-level list building.