Skip to content
webtype.orgwebtype.org#240 filter-tuple · par 3

    ↑↓ move · ⏎ open · esc close

    No. 240 · August 23, 2026 · Moderate

    Filter

    Implement `FilterTuple<T, U>` so it keeps only the elements assignable to `U`, preserving order and staying a tuple.

    01

    Try the puzzle yourself

    Par 3

    Puzzle

    filter-tuple.ts
    Stroke 1 of 3Not run yet

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

    Checks

    4
    • FilterTuple<[1, 'a', 2, 'b'], number>
      [1, 2]
    • FilterTuple<[], string>
      []
    • FilterTuple<['x'], number>
      []
    • FilterTuple<[1, 2, 3], number>
      [1, 2, 3]

    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 24, 2026

    The solution

    type FilterTuple<T extends readonly unknown[], U> =
      T extends readonly [infer H, ...infer R]
        ? H extends U
          ? [H, ...FilterTuple<R, U>]
          : FilterTuple<R, U>
        : []

    The common wrong answer

    type FilterTuple<T extends readonly unknown[], U> = Extract<T[number], U>[]

    This filters the right elements and then throws away everything that made them a tuple. `Extract` works on the union `1 | "a" | 2 | "b"` and returns `1 | 2`; the `[]` suffix turns that into `(1 | 2)[]` — an array of unknown length, not the two-element tuple `[1, 2]`.

    Line by line

    1. ? [H, ...FilterTuple<R, U>]

      The element passed, so it is placed at the front and the filtered tail spread after it. Order is preserved because each level contributes its own head before anything deeper.

    2. : FilterTuple<R, U>

      The element failed, so the recursion continues with nothing added. Skipping is simply the absence of a contribution — there is no "remove" operation at the type level.

    Takeaway

    Tuples and unions are not interchangeable. The moment you convert to a union to make filtering easy, you have lost length and order — and getting them back costs more than filtering the tuple directly.

    Uses