I thought I’d write up a short post to explain a TypeScript problem that took up far too much of my time.
The TL;DR is that if you have overload signatures for a function that takes a variable number of (initial) arguments, the ordering of the signatures matters in a way that is not obvious. And the ‘simplest’ signature should be placed first.
The types I was working with were in RxJS, but to keep things general let’s use an example that combines a bunch of arrays in some way, like this:
combine function will take elements from each of the input arrays that are passed — the caller can pass as many as is necessary — and will return an array containing the combined elements. (How they are combined doesn’t matter; we’re only interested in the types.)
combine could be typed like this:
Without going into too much detail,
Inputs is mapped type that maps a tuple of element types to a tuple of read-only arrays — for the input array parameters — like this:
And the element type of the returned array is obtained — from the
Elements type parameter — using this mechanism:
With the signature declared like this, the return value’s type is inferred correctly:
Let’s say the
combine function can also be passed a count to specify how many elements at a time should be taken from each input array. We can add an overload signature like this:
With these signatures, the return value’s type is still inferred correctly:
Let’s say we add another overload signature that allows the direction of the combination to be specified:
With these signatures, the return value’s type is inferred correctly for this usage:
However, there is a problem. If
combine is passed only a single input array, the return value’s type is not inferred correctly:
This doesn’t make much sense. The only overload signature that should be matched is the last one.
It turns out that the order of the overload signatures is significant. It shouldn’t be, but it is.
If they are ordered like this:
the inference is correct:
So the take away from this is that if you have overloaded varargs signatures, they should be ordered so that the first signature is the signature without trailing parameters.
This post was first published on my personal blog: ncjamieson.com.