TypeScript Infer keyword Explained

TypeScript Infer keyword Explained

Sunny Sun Lv4

What is infer keyword? What are the use cases?

The Infer keyword can be perplexing and challenging to grasp. This article aims to provide a clear and straightforward explanation of this concept.

In the official documentation, infer is mentioned in the Conditional Type s section:

Within the extends clause of a conditional type, it is now possible to have infer declarations that introduce a type variable to be inferred. Such inferred type variables may be referenced in the true branch of the conditional type. It is possible to have multiple infer locations for the same type variable.

If the above definition is as clear as mud to you, don’t worry, you’re not alone.

What is infer keyword?

The infer keyword allows you to deduce a type from another type within a conditional type. Here’s an example:

1
_type_ UnpackArrayType<T> = T _extends_ (infer R)[] ? R: T;_type_ t1 = UnpackArrayType<number[]>; // t1 is number

UnpackArrayType is a conditional type. It is read as “If T is a sub-type of _(infer R)[]_ , return _R_. Otherwise, return _T_”.

For type alias t1, the condition in UnpackArrayType is true because number[] matches with (infer R)[]. As the result of the infer process, the type variable R is inferred to be number type, and returned from the true branch. Infer is there to tell the compiler that a new type variable R is declared within the scope of UnpackArrayType.

1
_type_ t2 = UnpackArrayType<string>; //t2 is string

For t2 , the condition in UnpackArrayType is false, as the string type does not match with(infer R)[] , so it is returned as string.

Scope

Infer keyword only works within a conditional type. You will get an error if you try to use it outside the conditional type.

Here are a few examples of the infer keyword use cases.

Case 1: Extract type from Promise

1
2
3
type unboxFromPromise<T> = T extends Promise<infer R>? R : T;  
type t1 = Promise<string[]>;
let promiseType : unboxFromPromise<t1>; // **string[]**

In this example, we can deduce the result of promiseType from the equation:_Promise<string[]> = Promise<infer R>_

Thus the promiseType is string[].

Case 2: Extract function return type

1
2
3
type functionReturn<T> = T extends (…args: string[]) => infer R? R: T;  
type f1 = (a:string) => number;
type returnType = functionReturn<f1>; // **number**

The functionReturn will return the function return type asR type variable if the type passed in matches a function signature. If the condition is false, then T is returned.

Case 3: Extract multiple candidates

In TypeScript documentation, it says

multiple candidates for the same type variable in co-variant positions causes a union type to be inferred

1
2
type unboxFromObject<T> = T extends {a: infer R; b: infer R} ? R: never;  
type r1 = unboxFromObject<{a: string; b: number}>; // string | number

In other words, when a type is inferred for several values, the result is a union type. In the above example, the result is “string | number”.

multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.

1
type unboxFromObjectFunctions<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }? U: never;type r2= unboxFromObjectFunctions<{ a: (x: string) => void; b: (x: number) => void }>; // string & number

Use Infer Within a Recursive conditional type

The following example is a more advanced example of using infer with a recursive conditional type to flatten a nested array. Please note that the recursive type is only supported from TypeScript version 4.1.0 onwards.

1
2
3
4
5
type Flatten<T extends readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;
declare function flatRecurisve<T extends readonly unknown[]>(xs: T): Flatten<T>;
const t1 = flatRecurisve([‘apple’, [‘orange’, ‘pear’, 100],[[4, [true]]]] as const);
// readonly (true | ‘apple’| ‘orange’, ‘pear’, 100, 4)[]

I like the above example, as it shows how the TypeScript types can be used to write concise and self-explanatory code.

Summary

Infer keyword isn’t something you need to use daily, but it is widely used in advanced Utility types. Understanding it will help you use Utility types properly.

This is the 3rd article of my “TypeScript Types” series, and you can find the other two articles below.

TypeScript’s Record Type Explained

I hope you have found this useful. Happy programming!

  • Title: TypeScript Infer keyword Explained
  • Author: Sunny Sun
  • Created at : 2021-06-01 00:00:00
  • Updated at : 2024-08-16 19:46:32
  • Link: http://coffeethinkcode.com/2021/06/01/typescript-infer-keyword-explained/
  • License: This work is licensed under CC BY-NC-SA 4.0.