TypeScriptGenerics and advanced types
infer
Pulls a type out from inside another type, within a conditional type.
Example
#TypeScript
type ElementOf<T> = T extends (infer U)[] ? U : never
type Unwrap<T> = T extends Promise<infer U> ? U : T
type Tag = ElementOf<string[]>
// type Tag = string
type Data = Unwrap<Promise<{ level: number }>>
// type Data = { level: number }
type Plain = Unwrap<number>
// type Plain = number
const data: Data = { level: 7 }
console.log(data.level)Result
7
Definition and usage
#The infer keyword can only be used in the condition of a conditional type. It declares a new, local type parameter that TypeScript matches on its own. For example, T extends Promise<infer U> ? U : T pulls out the type of the value inside a promise.
That’s how the built-in utility types ReturnType, Parameters, and Awaited work. You can create your own: the type of an array element, the type of a callback’s argument, the type of a field in a nested object.
If the match fails, the conditional type falls through to the “false” branch, so it’s always worth thinking through what it should return in that case.
Syntax
#Syntax
type Result<T> = T extends Pattern<infer U> ? U : OtherMore examples
#TypeScript
type FirstArg<F> = F extends (first: infer A, ...rest: any[]) => any ? A : never
function greet(name: string, times: number) {
return `Hi ${name}! `.repeat(times).trim()
}
type Name = FirstArg<typeof greet>
// type Name = string
const who: Name = 'Mia'
console.log(greet(who, 2))Result
Hi Mia! Hi Mia!
Good practices
#- Before writing your own type with
infer, check the built-in ones:ReturnType,Parameters,Awaited,InstanceType. - Name
inferparameters descriptively when the type gets complex, e.g.infer TItem.
Related entries
#Spotted a mistake or missing an example? Write to us.