Skip to content
webtype.orgwebtype.org

    ↑↓ 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