Introduction

In this guide, we'll explore the role of the infer clause in TypeScript. The infer keyword is a powerful tool that allows you to create more flexible and reusable types by capturing and reusing type information within conditional types.

What is infer?

The infer keyword is used within the context of conditional types to infer a type variable from a given type. This is particularly useful when you want to extract specific types from complex structures, such as function parameters or return types.

Why Use infer?

Using infer can help you:

Basic Example

Let's start with a simple example to illustrate how infer works. Consider the following type:

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

In this example, we're using infer to capture the return type of a function. If T is a function type, R will be the inferred return type; otherwise, it will be never.

Practical Examples

Question #1:

Create a type GetElementType<T> that extracts the element type from a Set. For example, GetElementType<Set<number>> should be number.

app/page.tsx

type GetElementType<T> = T extends Set<infer U> ? U : T;

type NumberType = GetElementType<Set<number>>;  // number

type StringType = GetElementType<Set<string>>;  // string

type ObjectType = GetElementType<Set<{ name: string; email: string;}>>;  // { name: string; email: string; }

type BooleanType = GetElementType<Set<boolean>>;  // boolean

type NonSetType = GetElementType<number>;  // number

Question #2:

Create a type GetSecondParam<T> that extracts the second parameter type from a function. Return never if there's no second parameter. E.g.:

GetSecondParam<(a: string, b: number, c: boolean) => void> should be number.

GetSecondParam<(a: string) => void> should be never.

app/page.tsx

type GetSecondParam<T> = T extends (...args: [p1: any, p2: infer P, ...rest: any[]]) => any ? P : never;

type NumberType2 = GetSecondParam<(arg1: string, arg2: number) => void>;

type StringType2 = GetSecondParam<(arg1: string, arg2: string, arg3: boolean) => boolean>;

type Objectype2 = GetSecondParam<(arg1: string, arg2: {name: string; email: string;}, arg3: boolean) => boolean>;

type BooleanType2 = GetSecondParam<(arg1: string, arg2: boolean, arg3: boolean) => boolean>;

Question #3:

Create a type Flatten<T> that recursively unwraps nested arrays to get the innermost type. For example:

  • Flatten<number[][][]> should be number
  • Flatten<string[][]> should be string
  • Flatten<boolean[]> should be boolean
app/page.tsx

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

type FlattenA = Flatten<number[]>;// → number
type FlattenB = Flatten<string[][]>;// → string
type FlattenC = Flatten<boolean[][][]>;// → boolean
app/page.tsx

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

type BooleanFunction = ReturnType<() => boolean>;
type NumberFunction = ReturnType<(arg1: number, arg2: string) => number>;
type StringFunction = ReturnType<(arg: number) => string>;
app/page.tsx

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

type DeepBooleanFunction = ReturnTypeDeep<() => () => boolean>;
type DeepNumberFunction = ReturnTypeDeep<(arg1: number, arg2: string) => () => number>;
type DeepStringFunction = ReturnTypeDeep<(arg: number) => () => string>;

Conclusion

The infer keyword is a powerful feature in TypeScript that allows for more expressive and flexible type definitions. By leveraging infer, you can create types that adapt to the structure of your code, making it easier to work with complex types and improving overall type safety.

© 2026 - Mo Sayed