I'm confused by how types are inferred in the following example
type RetType<T> = T extends (...args: (infer I)[]) => infer R ? [I, R] : any;
type X = (a: number, b: string) => void;
type Q = RetType<X>;
If you hover over the type of Q
in the playground you will get [number & string, void]
. It's confusing because I would expect I
to be inferred as number | string
(union) instead of number & string
(intersection).
Does anyone understand why the input arguments are inferred as an intersection instead of a union?
TL;DR: Because whatever I
is, it has to be assignable to all the arguments of the function type T
.
This is because function arguments are contra-variant. That just means that for one function to be used in place of another, its argument type must be the same or more general than the other's. This is pretty obvious when you take a look at an example:
type f: (arg: string) => string;
type g: (arg: "foo") => string;
// f is assignable to g, since a function expecting
// to receive any string should have no problem accepting
// the specific string "foo".
// However, the reverse isn't true. You can't assign g to f,
// since g expects to receive exactly the string "foo" for its
// argument, but if it's used in place of f, it can receive any string.
In other words, f
is assignable to g
because g
's argument is assignable to f
's. That reversal is the contra part.
So if T
is a subtype of some mystery function type (...args: I[]) => R
, argument contra-variance tells us that I
must be assignable to the argument type of T
.
Thus, T extends (...args: (infer I)[]) => infer R
tells typescript to infer some single type I
such that I
can be used in place of any argument of T
.
So for your type X
, it should be true that whatever I
is, it must be assignable to both arguments. Since the argument types are respectively number
and string
, we ask: what type is assignable to both of those?
Well, number & string
.
*For more, you may be interested in reading about co and contra-variance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With