The infer Keyword
The infer Keyword
Level 9 — Advanced Types A compiler keyword used exclusively inside the
extendsclause of Conditional Types to declare a temporary type placeholder variable that the compiler automatically extracts from a matched type pattern.
1. Prerequisites
- Conditional Types — The type-level
if/elsechecks. - Generics Overview (
<T>) — Parametric type declarations.
2. Term Category
TypeScript Advanced Type (Pattern Matching Type Variable Inference): The infer keyword introduces a temporary type variable within a conditional type branch to extract constituent types automatically.
3. Explanation
Environment Context
- Build-time (Like all type-level calculations,
infervariables exist only during compilation and have zero runtime overhead).
4. Common Mistakes & Pitfalls
Mistake 1: Attempting to use infer outside of a conditional extends clause
The mistake: Declaring an infer variable inside a generic type parameter list or standard object type.
Why it's wrong: The infer keyword is strictly a pattern-matching operator. It has no meaning outside of a conditional type statement.
Incorrect:
// Error: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
type Logger<infer T> = { log: (val: T) => void };
Fix: Declare T as a standard generic parameter.
type Logger<T> = { log: (val: T) => void };
Golden Rule: The infer keyword can only be written after extends inside a conditional type. The captured type variable is only accessible in the "true" (left) branch of the conditional ternary.
Mistake 2: Using infer Outside Conditional Type extends Clauses
The mistake: Writing type Unpack<T> = infer U; (TS1338).
Why it's wrong: The infer keyword can ONLY be declared within the extends evaluation clause of a conditional type.
Incorrect:
// type Bad<T> = infer U; // ❌ 'infer' declarations are only permitted in the 'extends' clause of a conditional type
Fix:
type Unpack<T> = T extends (infer U)[] ? U : T; // Correct infer declaration
Mistake 3: Declaring Duplicate infer Identifiers in the Same Conditional Clause
The mistake: Re-using the same infer R variable name in incompatible positions without union intent.
Why it's wrong: Re-using the same infer R identifier across multiple covariant positions creates union inference, whereas contravariant positions create intersection inference.
Incorrect:
type Overloaded<T> = T extends (a: infer R, b: infer R) => void ? R : never;
Fix:
type Overloaded<T> = T extends (a: infer A, b: infer B) => void ? [A, B] : never;
5. Practice Exercises
Exercise 1: Extracting Promise Inner Value Types with infer
Scenario:
Create a custom UnwrapPromise<T> conditional type using infer to extract the inner resolved value of a Promise<T>.
Requirements:
- Define
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T.
Answer
Implementation
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type R1 = UnwrapPromise<Promise<string>>; // string
type R2 = UnwrapPromise<Promise<number>>; // number
type R3 = UnwrapPromise<boolean>; // boolean
Technical Explanation
infer Uintroduces a type variableUwithin theextendsclause of a conditional type.- If
TmatchesPromise<U>, the compiler infersUand makes it available in thetruebranch. - Pattern matching mechanism for extracting generic inner types.
Exercise 2: Extracting Array Element Types with infer
Scenario:
Extract the element type of an array using ArrayElement<T>.
Requirements:
- Define
type ArrayElement<T> = T extends (infer E)[] ? E : T.
Answer
Implementation
type ArrayElement<T> = T extends (infer E)[] ? E : T;
type E1 = ArrayElement<string[]>; // string
type E2 = ArrayElement<number[]>; // number
type E3 = ArrayElement<boolean>; // boolean
Technical Explanation
T extends (infer E)[]pattern matches array types and binds element typeE.- Returns the unwrapped element type
Efor arrays, or the original typeTfor non-arrays. - Reusable structural pattern matching utility.
Exercise 3: Extracting Function First Argument Types with infer
Scenario:
Extract the type of the first argument of any function using FirstArgument<T>.
Requirements:
- Define
type FirstArgument<T> = T extends (first: infer F, ...args: any[]) => any ? F : never.
Answer
Implementation
type FirstArgument<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
function handler(id: number, message: string) {}
type TargetType = FirstArgument<typeof handler>; // number
Technical Explanation
infer Fpattern matches function parameter tuples, capturing the first parameter's type.- Returns
neverifTis not a function. - Advanced type meta-programming with
infer.
6. Related Terms
- Conditional Types — The ternary structure that hosts
infer. ReturnType<T>— The utility type powered byinfer.Parameters/ConstructorParameters/Awaited— Standard library utilities built usinginfer.
7. Key Takeaways
- The
inferkeyword declares a type placeholder variable that the compiler resolves dynamically. - It can only be used inside the
extendsclause of a conditional type. - The inferred variable is only in scope inside the "true" branch of the conditional ternary.
- Used to construct complex, recursive extraction utilities (like
Awaited,Parameters, or custom framework types). - Enables clean, type-safe reflection on functional and object properties.