Implementing a Zip Function in TypeScript with Advanced Type Inference
This article walks through building a zip function that merges two arrays, adding basic TypeScript signatures, then demonstrates advanced generic type programming to infer exact tuple return types, handling readonly tuples, mutable mappings, and function overloads for precise type safety.
We start with a simple JavaScript‑style zip function that merges two arrays element‑by‑element, returning an array of pairs.
function zip(target, source) {
if (!target.length || !source.length) return [];
const [one, ...rest1] = target;
const [other, ...rest2] = source;
return [[one, other], ...zip(rest1, rest2)];
}Next we give the function a basic TypeScript signature using unknown[] for both parameters and the return type, and show two ways to declare the type – a function declaration and an arrow‑function variable.
function zip(target: unknown[], source: unknown[]): unknown[];
const zip = (target: unknown[], source: unknown[]): unknown[];The third layer introduces advanced type programming: a recursive conditional type Zip<One, Other> extracts the first element of each tuple, builds a pair, and recurses on the rest, producing an exact tuple type for the result.
type Zip
=
One extends [infer OneFirst, ...infer Rest1]
? Other extends [infer OtherFirst, ...infer Rest2]
? [[OneFirst, OtherFirst], ...Zip
]
: []
: [];Because literal arrays are inferred as readonly tuples, we define a Mutable<T> mapped type to strip the readonly modifier.
type Mutable
= { -readonly [Key in keyof Obj]: Obj[Key] };We then overload the zip function: a generic overload that returns Zip<Mutable , Mutable > , followed by the non‑generic implementation.
function zip
(target: Target, source: Source): Zip
, Mutable
>;
function zip(target: unknown[], source: unknown[]): unknown[] {
if (!target.length || !source.length) return [];
const [one, ...rest1] = target;
const [other, ...rest2] = source;
return [[one, other], ...zip(rest1, rest2)];
}Finally we demonstrate usage with literal arrays (using as const ) and with regular arrays, showing that the inferred return type matches the exact paired tuple when possible.
const result = zip([1,2,3] as const, [4,5,6] as const);
// result: [[1,4],[2,5],[3,6]]
const arr1 = [1,2,3];
const arr2 = [4,'5',6];
const result2 = zip(arr1, arr2);
// result2: (number | string)[][]IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.