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

    ↑↓ move · ⏎ open · esc close

    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.

    Short game

    Fewest characters

    No public scores yet.

    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.

    Uses