TypeScriptUtility types

NoInfer<Type>

Blocks inferring a type parameter from a given spot, so other arguments determine the type.

On this page

Example

#
TypeScript
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"'.
Result
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

#
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

#
Without NoInfer, a typo gets through
TypeScript
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)
Result
expert
The approach before TypeScript 5.4
TypeScript
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 NoInfer when 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, NoInfer isn’t needed. Add it only once inference from one of the arguments produces too wide a type.

Related entries

#

Spotted a mistake or missing an example? Write to us.