Annotated solution
Published August 24, 2026The 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
? [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.
: 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.

