DEV Community

Cover image for A Deep Dive into TypeScript's infer Keyword
Leapcell
Leapcell

Posted on

A Deep Dive into TypeScript's infer Keyword

Cover

The infer keyword in TypeScript is used in conditional types to infer a type. This is particularly useful when dealing with complex types, allowing us to extract or transform types.

Basic Usage

The infer keyword can only be used within conditional types, typically in conjunction with generics and the extends keyword. The syntax is as follows:

type Moment<T> = T extends infer U ? U : never;
Enter fullscreen mode Exit fullscreen mode

Here, T extends infer U means that we attempt to infer the type T and assign it to U. If type inference succeeds, then U becomes the inferred type.

We can use it to infer different types. Here are a few examples:

type Moment<T> = T extends infer U ? U : never;

type StringType = Moment<string>; // string
type NumberType = Moment<number>; // number
type UnionType = Moment<string | number>; // string | number

interface User {
  name: string;
  age: number;
}

type UserType = Moment<User>; // User
Enter fullscreen mode Exit fullscreen mode

In these examples, Moment<T> essentially just returns the type T without any conversion or processing. This primarily serves to demonstrate the basic usage of conditional types and type inference.

Common Examples

Extracting a Function’s Return Type

Suppose we have a function type and we want to extract its return type. We can do this:

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

type ExampleFunction = (x: number, y: string) => boolean;
type ReturnTypeOfExampleFunction = GetReturnType<ExampleFunction>; // boolean
Enter fullscreen mode Exit fullscreen mode

In the code above:

  1. T extends (...args: any[]) => infer R: This checks whether T is a function type. (...args: any[]) means the function can accept any number of arguments.
  2. infer R: If T is a function type, then infer R infers the function’s return type and assigns it to type variable R.
  3. ? R : never: If T is a function type, the inferred return type R is returned; otherwise, never is returned.

Extracting an Array’s Element Type

We can also use infer to extract the element type of an array:

type GetArrayElementType<T> = T extends (infer U)[] ? U : never;

type Moment = string[];
type Example1Array = Array<string>;

type ElementTypeOfExampleArray = GetArrayElementType<Moment>; // string
type ElementTypeOfExample1Array = GetArrayElementType<Example1Array>; // string
Enter fullscreen mode Exit fullscreen mode

Here, we use T extends (infer U)[] to infer the element type U of the array. Since T is string[], U becomes string.

string[] extends (infer U)[ ]
Enter fullscreen mode Exit fullscreen mode

It is important to note that infer declarations are only allowed within the extends clause of conditional types, and the type variable declared with infer is only available within the true branch.

Advanced Examples

Extracting the Value Type of a Promise

If we have a Promise type, we can extract its resolved value type:

type GetPromiseValueType<T> = T extends Promise<infer U> ? U : never;

// Example
type ExamplePromise = Promise<number>;
type ValueTypeOfExamplePromise = GetPromiseValueType<ExamplePromise>; // number
Enter fullscreen mode Exit fullscreen mode

In the code above:

  1. T extends Promise<infer U>: This checks whether T is a Promise type.
  2. infer U: If T is a Promise type, infer U infers the resolved value type of the Promise and assigns it to U.
  3. ? U : never: If T is a Promise type, the inferred value type U is returned; otherwise, never is returned.

Extracting a Function’s Parameter Types

Sometimes, we need to obtain the parameter types of a function. We can use infer to achieve this:

type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

type ExampleFunction = (a: number, b: string) => void;
type Params = GetParameters<ExampleFunction>; // [number, string]
Enter fullscreen mode Exit fullscreen mode

In the code above:

  1. T extends (...args: infer P) => any: This checks whether T is a function type.
  2. infer P: If T is a function type, infer P infers the function’s parameter types and assigns them to P.
  3. ? P : never: If T is a function type, the inferred parameter types P are returned; otherwise, never is returned.

Extracting Constructor Parameter Types

We can also use infer to extract the parameter types of a class constructor:

type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;

class ExampleClass {
  constructor(public a: number, public b: string) {}
}

type Params = ConstructorParameters<typeof ExampleClass>; // [number, string]
Enter fullscreen mode Exit fullscreen mode

Complex Inference in Conditional Types

Suppose we need to use complex inference logic within a conditional type:

type IsArray<T> = T extends (infer U)[] ? U : never;
type IsFunction<T> = T extends (...args: any[]) => infer R ? R : never;

type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;

// Example
type ArrayType = ExtractType<string[]>; // string
type FunctionReturnType = ExtractType<() => number>; // number
type DefaultType = ExtractType<boolean>; // boolean
Enter fullscreen mode Exit fullscreen mode

In this code:

type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;
Enter fullscreen mode Exit fullscreen mode
  1. T extends any[] ? IsArray<T>: If T is an array type, return IsArray, which extracts the element type of the array.
  2. T extends (...args: any[]) => any ? IsFunction<T>: If T is a function type, return IsFunction, which extracts the function’s return type.
  3. T: If T is neither an array type nor a function type, return T itself.

Summary

The infer keyword is used in conditional types to infer a new type variable from another type. It allows extracting and utilizing specific subtypes or properties during type checking, enhancing the expressiveness and flexibility of TypeScript’s type system. In simple terms, infer helps automatically extract the desired parts from complex types.


We are Leapcell, your top choice for hosting Node.js projects.

Leapcell

Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:

Multi-Language Support

  • Develop with Node.js, Python, Go, or Rust.

Deploy unlimited projects for free

  • pay only for usage — no requests, no charges.

Unbeatable Cost Efficiency

  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.

Streamlined Developer Experience

  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.

Effortless Scalability and High Performance

  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Explore more in the Documentation!

Try Leapcell

Follow us on X: @LeapcellHQ


Read on our blog

Top comments (0)