Intermediate introduced as const. A const type parameter applies a similar literal-preservation preference to an inferred call argument. In function define<const T>(value:T):T, an inline literal can retain its precise readonly shape. This is a checking decision, not Object.freeze.
Preserve a literal before controlling inference
Compare ordinary generic inference with const inference on an inline record. The ordinary record’s mode widens to string; the const version retains the literal “safe” and a readonly property. An annotation of exactly “safe” lets you check the difference without printing a type.
function remember<T>(value: T): T {
return value;
}
function keep<const T>(value: T): T {
return value;
}
const ordinary = remember({ mode: "safe" });
const precise = keep({ mode: "safe" });
ordinary.mode = "fast";
const exact: "safe" = precise.mode;
console.log(ordinary.mode, exact);Enable JavaScript to edit and type-check this example.
Check your prediction
fast safe Choose which argument supplies evidence
Inference can also gather evidence from more than one argument. If the first argument supplies allowed modes and the second is a default, you may want only the allowed list to determine T. Built-in NoInfer<T> prevents that second position from contributing candidates, while still checking its compatibility with T.
Without NoInfer, an incompatible fallback can help widen T. For example, a helper with fallback: T can infer “safe” | “fast” | “typo” from pick(["safe", "fast"], "typo"). That compiles because inference considered both arguments. Our desired rule is stricter: the first argument defines the choices, and the second must fit them. The error lab tests that rule.
Read the pickMode signature in order: T must be a string, allowed provides readonly choices, and fallback must be one of the inferred choices. The runtime includes check remains useful for untyped callers or invalid assertions. NoInfer does not validate an arbitrary value from JSON.
These features are available in the pinned TypeScript 5.9.3 compiler. Use them when they improve a real caller contract; an ordinary annotation is often enough. Passing an already widened string[] cannot recover its original literal members by adding const to a later function.
Predict, check, and run
Write the output before running. Use Check types first, then Check & run. Types disappear at runtime: explain both what the compiler checks and what the code actually does. Each editor is an independent ES module; await asynchronous work explicitly.
function pickMode<const T extends string>(
allowed: readonly T[],
fallback: NoInfer<T>
): T {
if (!allowed.includes(fallback)) {
throw new Error("Invalid fallback");
}
return fallback;
}
console.log(pickMode(["safe", "fast"], "safe"));Enable JavaScript to edit and type-check this example.
Check your prediction
safe Read the compiler error
Predict the rejected relationship before checking. Read the real diagnostic and its source location; fix the contract or the value instead of hiding it with an assertion.
function choose<const T extends string>(
choices: readonly T[],
fallback: NoInfer<T>
): T {
return fallback;
}
console.log(choose(["small", "large"], "medium"));Enable JavaScript to edit and type-check this example.
Check your diagnosis
The first argument determines small|large. NoInfer stops medium from expanding that set through the fallback argument.
Build it yourself
Literal Inference & NoInfer: practise
Write choose<const T extends string> with readonly choices and a NoInfer<T> fallback. Choose small from [small,large], print small, then verify medium is a compiler error. Keep a runtime membership check.
// Start from the stated contract. Add a valid call and an edge case.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
Let the list determine the allowed literal union.
Hint 2: a first step
Use NoInfer only around the fallback type, not around the choices.
Show one solution
function choose<const T extends string>(
choices: readonly T[],
fallback: NoInfer<T>
): T {
if (!choices.includes(fallback)) {
throw new Error("Invalid choice");
}
return fallback;
}
console.log(choose(["small", "large"], "small"));Enable JavaScript to edit and type-check this example.
smallTrace the implementation against the task before comparing styles. A passing example supports that case; use the changes below to challenge the wider contract.
Change it and test it
Assign the list to a string[] variable before calling choose and observe how the contract widens. Compare this with an as const list and explain why inference cannot recover discarded information.
Completion is saved in this browser only. It does not measure mastery.