What is Infer in TypeScript and What Does It Do?

technology2 min

byLucas Santos

This page was machine translated. Read original / Suggest a fix

If you’ve already used TypeScript, you’ve probably heard of the infer keyword. It’s not very common in everyday coding, but most advanced libraries will use infer at some point for some type of operation.

To fully understand infer, we need to have a notion of how TypeScript does type assertion and also the hierarchy of those types. I won’t go into detail about this information here, but you can find plenty of content about it in the TS documentation itself.

The infer keyword complements what we call conditional typing, or conditional types, which is when we have a type inference followed by a condition, for example:

type NonNullable<T> = T extends null | undefined ? never : T

In the previous example, we’re taking a type and checking if it’s an extension of null or undefined, that is, types that don’t resolve to true, and then we’re making a type condition to say: “If the type is one of these you return never, otherwise you return the type itself”.

The infer keyword allows us to go a bit further than what we’re used to in these models. The idea is that we can define a variable within our type inference that can be used or returned, it’s as if we could do a const tipo = <inferencia>.

For example, let’s look at the native TS utility called ReturnType, which takes a function as a parameter and returns the type of its return value:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any

What’s happening here is a conditional inference, since infer cannot be used outside conditionals. First we check if the type passed extends a function signature, if yes, we assign the return of that function to a variable we call R, and then return it.

Another example is extracting the return of a promise, as I mentioned here in this thread, if we think about how we can create this type, first we have to check if the type passed is an extension of the Promise<T> type, and then infer T to return it, otherwise we return never:

type Unpromise<P> = P extends Promise<infer T> ? T : never

Other Use Cases#

We can use infer in several cases, the most common ones are:

  • Get the first parameter of a function:
type FirstArgument<T> = T extends (first: infer F, ...args: any[]) => any ? F : never
  • Get the type of an array
type ArrayType<T> = T extends (infer A)[] ? A : T
  • Recursively get the type of a function until finding its final type
type ExtractType<T> = T extends Promise<infer R>
? R
: T extends (...args: any[]) => any
? ExtractType<ReturnType<T>>
: T