Skip to content
webtype.org#224 join-tuple · par 4

No. 224 · August 1, 2026 · Hard

Join

Implement `Join<T, D>` so it concatenates a tuple of strings with the delimiter `D` between them. No delimiter before the first element or after the last.

01

Try the puzzle yourself

Par 4

Puzzle

join-tuple.ts
Stroke 1 of 4Not run yet

Replace ??? — your solution is checked against the cases below. Tab indents; press Escape then Tab to move focus out.

Checks

4
  • Join<['a', 'b', 'c'], '-'>
    'a-b-c'
  • Join<['solo'], '-'>
    'solo'
  • Join<[], '-'>
    ''
  • Join<['x', 'y'], ''>
    'xy'

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

How everyone did

Fewer than 5 people have solved this one so far. The distribution appears once there is enough of a sample to mean anything.

Archive
02

Annotated solution

Published August 2, 2026

The solution

type Join<T extends readonly string[], D extends string> =
  T extends readonly [infer F extends string, ...infer R extends string[]]
    ? R extends readonly []
      ? F
      : `${F}${D}${Join<R, D>}`
    : ''

The common wrong answer

type Join<T extends readonly string[], D extends string> =
  T extends readonly [infer F extends string, ...infer R extends string[]]
    ? `${F}${D}${Join<R, D>}`
    : ''

Every level appends a delimiter, including the last one, so `["a","b","c"]` joins to `"a-b-c-"`. Joining is not "put a separator after each item" — it is "put a separator *between* items", and the difference only shows at the end.

Line by line

  1. infer F extends string

    The `extends string` constraint on an `infer` narrows what is bound. Without it `F` would be `unknown`, which cannot be interpolated into a template literal type.

  2. R extends readonly [] ? F

    The guard that makes it a join rather than a suffix. When nothing follows, the element is emitted bare and the recursion stops without contributing a delimiter.

Takeaway

Recursive string building almost always needs a distinct base case for the final element. If your output has a stray delimiter on the end, that case is missing.