Annotated solution
Published July 26, 2026The 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
`${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.
[Head, ...Split<Tail, D>]The spread inlines the recursive tuple, so each level contributes one element to a flat result.
: [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.