Mastering TypeScript’s infer: How to Extract Types Automatically
This article explains the TypeScript infer keyword, how it works within conditional types to automatically extract type information, and provides detailed examples for array element inference, function return types, and parameter extraction, highlighting its benefits for flexible and type‑safe code.
The infer feature was introduced in TypeScript 2.8 via a pull request submitted by Anders Hejlsberg, Microsoft’s chief architect of TypeScript.
What is infer
The infer keyword is used inside conditional types to declare a type variable and let the compiler infer that type from the type being examined. It extracts information from type relationships to satisfy the logic of conditional types.
Key points:
It can only be used in the extends clause of a conditional type.
It declares a type variable that receives the inferred type.
It extracts a concrete type from a matching type, similar to destructuring function parameters.
In short, infer acts as a type placeholder that automatically finds and extracts the desired type within complex type expressions.
Example analysis
Case 1: Array type inference
<code>type ArrayElement<T extends any[]> = T extends (infer Element)[] ? Element : never;</code>Result:
<code>type NumArray = number[];
type StrArray = string[];
type BoolArray = boolean[];
type EmptyArray = never[];
type NotArray = 42;
type First = ArrayElement<NumArray>; // number
type Second = ArrayElement<StrArray>; // string
type Third = ArrayElement<BoolArray>; // boolean
type Empty = ArrayElement<EmptyArray>; // never (because never[] elements are never)
type Not = ArrayElement<NotArray>; // never (because NotArray is not an array type)
</code>Explanation of the type breakdown:
T extends any[] ensures that T is an array type.
T extends (infer Element)[] uses infer to capture the element type of the array as Element .
The ? Element branch returns the inferred element type when the condition is true.
The : never branch yields never when T is not an array.
Case 2: Function return type
The built‑in ReturnType utility is defined as:
<code>type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;</code>Result:
<code>type Func1 = () => number;
type Func2 = (a: string, b: number) => boolean;
type Func3 = (x: any) => void;
type Func4 = (message: string) => Promise<string>;
type Func5 = <T>(item: T) => T[];
type Returned1 = ReturnType<Func1>; // number
type Returned2 = ReturnType<Func2>; // boolean
type Returned3 = ReturnType<Func3>; // void
type Returned4 = ReturnType<Func4>; // Promise<string>
type Returned5 = ReturnType<Func5>; // T[]
</code>Explanation of the type breakdown:
T extends (...args: any[]) => any constrains T to be a function type.
T extends (...args: any[]) => infer R uses infer to capture the function’s return type as R .
The ? R branch yields the inferred return type when the condition holds.
The : any branch provides a fallback type, though it never occurs because of the preceding constraint.
Parameters utility
TypeScript also provides a built‑in Parameters utility to extract a function’s parameter types as a tuple:
<code>type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;</code>Example:
<code>type Func1 = (a: number, b: string) => void;
type Func2 = (x: boolean, y: number, z: string) => boolean;
type Params1 = Parameters<Func1>; // [number, string]
type Params2 = Parameters<Func2>; // [boolean, number, string]
</code>If infer is unavailable
Without infer , a similar result for ReturnType<T> would require a long chain of conditional types that manually match each possible return type:
<code>type GetReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => string ? string :
T extends (...args: any[]) => number ? number :
T extends (...args: any[]) => boolean ? boolean :
any;
// Tests
const test1: GetReturnType<() => number> = 123; // inferred as number
const test2: GetReturnType<() => string> = "Hello"; // inferred as string
const test3: GetReturnType<() => boolean> = true; // inferred as boolean
</code>This manual approach shows why infer is valuable—it saves a lot of repetitive type matching.
Summary
infer is TypeScript’s “automatic type extractor”. By placing it in an extends clause, you let the compiler infer a specific type and fill it in for you, improving flexibility, reusability, and type safety.
Flexibility: Provides a versatile way to manipulate and construct types, especially with generics.
Code reuse: Enables creation of generic utility types that can be reused across many scenarios.
Type safety: Helps ensure correctness of types and reduces the chance of type errors.
Remember that infer can only be used in an extends clause; using it elsewhere results in a compilation error.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.