Annotated solution
Published August 10, 2026The solution
type Zip<A extends readonly unknown[], B extends readonly unknown[]> = A extends readonly [infer AH, ...infer AR] ? B extends readonly [infer BH, ...infer BR] ? [[AH, BH], ...Zip<AR, BR>] : [] : []
The common wrong answer
type Zip<A extends readonly unknown[], B extends readonly unknown[]> = A extends readonly [infer AH, ...infer AR] ? B extends readonly [infer BH, ...infer BR] ? [AH, BH, ...Zip<AR, BR>] : [] : []
Without the inner brackets the pairs are spread flat into the result: `[1, "a", 2, "b"]` instead of `[[1, "a"], [2, "b"]]`. Zipping is about structure, and the structure lives in those brackets.
Line by line
[[AH, BH], ...Zip<AR, BR>]The inner `[AH, BH]` is one pair; the spread appends the pairs from the rest. Nesting a tuple inside a tuple is how you keep the pairing visible in the type.
: []Both fall-through branches end the recursion, so whichever tuple runs out first stops the zip. That gives the shorter-of-the-two behaviour without measuring either length.
Takeaway
Walking two structures at once is just two destructuring conditionals nested. The interesting design decision is what happens when they disagree in length — and here, doing nothing special is the answer.