TypeScript infer Keyword

infer is a keyword in TypeScript conditional types, used to infer new types from existing types.

It allows extracting and deriving types within generic conditional types, enabling powerful type operations.


SVG Diagram: How infer Works Background Title How the infer keyword works Original Type Original Type type Promise<T> = { value: T } Arrow infer inference Inference result Extracted type type ValueOf<T> = T extends Promise<infer V> ? V : never Example Example ValueOf<Promise<string>> → string Lower half: Application scenarios Common use cases for infer Scenario 1 Extract Promise value type Scenario 2 Extract array element type Scenario 3 Extract function return type Arrow marker

Why is the infer keyword needed?

In generic programming, we often need to extract partial types from complex types.

For example, fromPromise<string>extractstring, fromArray<User>extractUser。

The infer keyword makes such type extraction elegant and type-safe. It allows declaring a type variable in a conditional type, then using it in the true branch.

Concept explanation:infer is the abbreviation of "infer", meaning "to infer". It can only be used in the extends clause of a conditional type to declare a type variable to be inferred.


Basic Usage

Use infer to infer types in conditional types.

Example

// Extract the value type from Promise
// If T is Promise<V>, return V; otherwise return never
type ValueOf<T> = T extends Promise<infer V> ? V : never;

// Test
type Str = ValueOf<Promise<string>>;  // string
type Num = ValueOf<Promise<number>>; // number
type NotPrm = ValueOf<string>;        // never

// Usage example
var promise: Promise<string> = Promise.resolve("hello");
var value: ValueOf<typeof promise> = "world";

console.log("String extraction: " + (typeof value === "string" ? "Success" : "Failure"));

Output:

String extraction: success

Explanation:infer VA type variable V is declared, and TypeScript will automatically infer the type of V.


Extracting Array Element Types

Extract the element type from an array type.

Example

// Extract the element type of an array
// If T is an array V[], return V
type ArrayElement<T> = T extends (infer V)[] ? V : never;

// Test
type User = { name: string };
type Users = User[];

type E1 = ArrayElement<string[]>;   // string
type E2 = ArrayElement<Users>;       // User
type E3 = ArrayElement<number>;       // never (non-array)

// Usage example
var users: Users = [{ name: "Alice" }];
var element: ArrayElement<Users> = users[0];

console.log("Element type: " + element.name);

Use cases:This pattern is often used to extract elements of a specific type from union types.


Extracting Function Return Types

Extract the return type from a function type.

Example

// Extract the return type of a function
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

// Test
function getData() {
    return { id: 1, name: "Alice" };
}
function fetchUser(id: number): Promise<User> {
    return Promise.resolve({ id, name: "Bob" });
}

type R1 = ReturnType<typeof getData>;  // { id: number; name: string }
type R2 = ReturnType<typeof fetchUser>; // Promise<User>

// Usage example
var result: R1 = { id: 1, name: "Test" };
console.log("Return type: " + JSON.stringify(result));

Built-in types:TypeScript's built-inReturnType<T>utility types are implemented using infer.


Extracting Function Parameter Types

Extract parameter types from a function type.

< h2 class="example">Example
// Extract the type of the first parameter of a function
type FirstParameter<T> = T extends (first: infer P, ...rest: any[]) => any ? P : never;

// Test
function createUser(name: string, age: number): User {
    return { id: 1, name };
}
function logMessage(msg: string): void {
    console.log(msg);
}

type P1 = FirstParameter<typeof createUser>; // string
type P2 = FirstParameter<typeof logMessage>;  // string
type P3 = FirstParameter<() => void>;          // never

// Usage example
var nameParam: P1 = "Alice";
console.log("Parameter type: " + nameParam);

Note:You can use...rest: any[]to match any number of remaining parameters.


Multiple infer

Using multiple infer in a conditional type.

Example

// Extract the types of the first two elements of a tuple
type FirstTwo<T> = T extends [infer A, infer B, ...rest: any[]] ? [A, B] : never;

// Test
type Tuple = [string, number, boolean];
type FirstTwoTypes = FirstTwo<Tuple>; // [string, number]

// Extract object properties
type ObjectValue<T> = T extends { value: infer V } ? V : never;

type WithValue = { value: string; name: string };
type ExtractedValue = ObjectValue<WithValue>; // string

// Usage example
var tupleResult: FirstTwoTypes = ["hello", 123];
console.log("Tuple: " + JSON.stringify(tupleResult));

Tuple:Use...rest: any[]to match the remaining elements of a tuple.


Using infer in Recursive Types

Use infer in recursive type utilities to implement complex type operations.

Example

// Deep readonly type - recursively applying infer
type DeepReadonly<T> = T extends Function
    ? T
    : T extends object
        ? { readonly [P in keyof T]: DeepReadonly<T[P]> }
        : T;

// Test
interface User {
    name: string;
    address: {
        city: string;
        zip: string;
    };
}

type ReadonlyUser = DeepReadonly<User>;

// Flatten Promise types
type FlattenPromise<T> = T extends Promise<infer U>
    ? U extends Promise<any>
        ? FlattenPromise<U>
        : U
    : T;

type Nested = Promise<Promise<string>>;
type Flat = FlattenPromise<Nested>; // string

// Usage example
var user: ReadonlyUser = {
    name: "Alice",
    address: { city: "Beijing", zip: "100000" }
};
// user.name = "Bob"; // Error: readonly
console.log("Deep readonly: " + user.name);

Recursive infer:Using infer in recursive types enables deep type transformations, such as deep readonly, deep partial, etc.


Notes

  • Can only be used on the right side of extends:infer can only be used in the extends clause of a conditional type
  • Declaring type variables:Useinfer Vto declare the type variable to be inferred.
  • Multiple uses allowed:Multiple infer can be used in a single conditional type
  • Returns never if inference fails:If inference fails, the conditional type returns never

Best practices:infer is the core of implementing custom utility types; mastering it enables powerful type operations.


Summary

infer is one of the most powerful features in the TypeScript type system.

  • Type extraction:Extracting partial types from complex types
  • Conditional inference:Inferring types within conditional types
  • Function-related:Extracting function parameters and return types
  • Recursive utilities:Implementing deep type transformations

Recommendation:A deep understanding of infer allows you to write more advanced type utilities, improving type safety and development efficiency.

Other Extensions