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;
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
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
In the code above:
-
T extends (...args: any[]) => infer R
: This checks whetherT
is a function type.(...args: any[])
means the function can accept any number of arguments. -
infer R
: IfT
is a function type, theninfer R
infers the function’s return type and assigns it to type variableR
. -
? R : never
: IfT
is a function type, the inferred return typeR
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
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)[ ]
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
In the code above:
-
T extends Promise<infer U>
: This checks whetherT
is aPromise
type. -
infer U
: IfT
is aPromise
type,infer U
infers the resolved value type of thePromise
and assigns it toU
. -
? U : never
: IfT
is aPromise
type, the inferred value typeU
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]
In the code above:
-
T extends (...args: infer P) => any
: This checks whetherT
is a function type. -
infer P
: IfT
is a function type,infer P
infers the function’s parameter types and assigns them toP
. -
? P : never
: IfT
is a function type, the inferred parameter typesP
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]
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
In this code:
type ExtractType<T> = T extends any[]
? IsArray<T>
: T extends (...args: any[]) => any
? IsFunction<T>
: T;
-
T extends any[] ? IsArray<T>
: IfT
is an array type, returnIsArray
, which extracts the element type of the array. -
T extends (...args: any[]) => any ? IsFunction<T>
: IfT
is a function type, returnIsFunction
, which extracts the function’s return type. -
T
: IfT
is neither an array type nor a function type, returnT
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 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!
Follow us on X: @LeapcellHQ
Top comments (0)