NoInfer<Type>
Blocks inferring a type parameter from a given spot, so other arguments determine the type.
Example
#function createSelect<T extends string>(options: T[], initial: NoInfer<T>) {
return { options, selected: initial }
}
const difficulty = createSelect(['easy', 'medium', 'hard'], 'medium')
// T = "easy" | "medium" | "hard", inferred only from options
console.log(difficulty.selected)
createSelect(['easy', 'medium', 'hard'], 'expert')
// Error: Argument of type '"expert"' is not assignable to parameter of type '"easy" | "medium" | "hard"'.medium
Definition and usage
#NoInfer<Type> marks a position the compiler shouldn’t infer a type parameter from. The type itself doesn’t change: NoInfer<string> is still string. What changes is only where TypeScript takes its information from when it determines T for a call to a generic function.
A typical situation is a function with a list of allowed values and an initial value. If both parameters have the type T, the compiler infers T from both arguments, so a typo in the initial value silently widens the union instead of being reported as an error. With NoInfer<T> on the initial value, the type comes only from the list.
The type has been available since TypeScript 5.4. In older versions, the same effect comes from a second type parameter with a constraint, e.g. <T extends string, D extends T>.
Syntax
#function name<T>(source: T[], other: NoInfer<T>) { … }Type parameters
#Type
any type
The type that stays unchanged, but doesn’t take part in inference.
More examples
#function createSelectLoose<T extends string>(options: T[], initial: T) {
return { options, selected: initial }
}
const loose = createSelectLoose(['easy', 'medium', 'hard'], 'expert')
// T = "easy" | "medium" | "hard" | "expert", no error
console.log(loose.selected)expert
function createSelectOld<T extends string, D extends T>(options: T[], initial: D) {
return { options, selected: initial }
}
createSelectOld(['easy', 'medium', 'hard'], 'expert')
// Error: Argument of type '"expert"' is not assignable to parameter of type '"easy" | "medium" | "hard"'.Good practices
#- Use
NoInferwhen one argument should determine the type, and the others should only be checked against it, e.g. an initial value with a list of options. - In most generic functions,
NoInferisn’t needed. Add it only once inference from one of the arguments produces too wide a type.
Related entries
#- Generics <T>Types and functions with a type parameter that work for many types without losing precision.
- Generic constraints (extends)Requiring a type parameter to match a specific shape.
- Type inferenceThe compiler figuring out types automatically, without you writing annotations.
- Literal typesTypes that allow only specific values, e.g. "easy" | "hard".
- Default type parametersThe value a type parameter takes when it isn’t provided or inferred.
Spotted a mistake or missing an example? Write to us.