Typescript: How can I type tuples of unknown size with varying types?

Viewed 862

I couldn't find this in the similar questions.

I'm trying to type a tuple with an unknown number of elements.
I know Typescript 4 added support for Variadic Tuples

I can't wrap my head around it although I read the docs

The example is plain:


function map<S, T, U, Z>(values: [S, T, U], mapper: (s: S, t: T, u: U) => Z): Z {
    return mapper(...values);
}


const v = map([1, 'hello', 3], (s, t, u) => `${s + t + u}`);

Typescript playground link

This works for 3 elements but not for any (dynamic) number of elements.
How can I type this in a generic way for any number of elements?

Thanks!

1 Answers

Your data represents some extension of an array so have a generic like Elems extends unknown[] then the trick is to specify [...Elems] as the first argument instead of just Elems this indicates to typescript it should try to retain any tuple-ness* of the input:

function map<Elems extends unknown[],  Z>(values: [...Elems], mapper: (...args: Elems) => Z): Z {
    return mapper(...values);
}


const v = map([1, 'hello', 3], (s, t, u) => `${s + t + u}`);

(playground link)

*when I say it retains tuple-ness this is just a bi-product of how generic resolution works, if I have 2 functions like this:

function foo<T               >(arg: T){ return arg; }
function bar<T extends string>(arg: T){ return arg; }

calling foo("a") will give type string but bar("a") gives type "a", this is because typescript will get more specific to fit the generic constraint if possible, so while Elems is functionally identical to [...Elems], the first is considered an array type and the second is considered a tuple type which is why the generic resolves differently, retaining the tuple-ness.

Related