Ler em português

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.