How Typescript infer works
The infer
keyword was a new addition to Typescript 2.8. It is useful to extract a type from another type definition. It must be used along with conditional types.
Let's take the example from the official Typescript site to analyze:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
At a first glance, I couldn't understand that properly. Why the infer
key is needed if it just returns the same type from the function returned value? Well, the point is that when using infer
, Typescript will actually try to infer a type on R
. If this type exists, then it satisfies the condition and return it. Otherwise any
type is returned:
type A = ReturnType<() => User>; // User
type B = ReturnType<() => number>; // number
type C = ReturnType<() => stirng>; // any
The point is that without infer
, this sentence will always return R
, regardless of its type.
Multiple candidates
In some cases, it is possible to infer multiple results to the same type. In these cases, the type result can be either a union or a intersection.
When they are in co-variant positions, the result type is a union:
type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type FooProps = Foo<{ a: string; b: number }>; // string | number
When they are in contra-variant positions, the result type is an intersection:
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type BarPropsParams = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number
And when there is an overload, the last signature is the winner:
declare function foo(x: number): string;
declare function foo(x: string | number): string | number;
type FooReturnType = ReturnType<typeof foo>; // string | number
Conclusion
Got the idea? With infer you can actually get some type from a sentence without knowing it. It may be very valuable for libraries, on scenarios where types cannot be predicted.