Lesson 6 of 80
TypeScript infers types from values without annotations — but it 'widens' literals by default, and as const is how you opt out.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
What type does TypeScript infer for `let x = 5`, compared to `const x = 5`?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
TypeScript infers types from the value itself, without you writing an annotation everywhere. But it deliberately widens what it infers: let x = 5 infers number, not the literal 5 — because x could be reassigned to any other number later, so TypeScript generalizes.
const x = 5 is more precise — since x can never be reassigned, TypeScript infers the literal type 5. But that precision doesn't automatically extend to arrays and objects: const arr = [1, 2, 3] still infers number[], not the exact tuple [1, 2, 3], because array contents remain mutable even though arr itself can't be reassigned.
as const for a fixed set of valuesconst config = { mode: "dark", retries: 3 };
// inferred: { mode: string; retries: number } — widened
const configConst = { mode: "dark", retries: 3 } as const;
// inferred: { readonly mode: "dark"; readonly retries: 3 } — exact literals, deeply readonly
as const tells TypeScript "infer the narrowest possible type here, and lock it." That's exactly what you want when a value is meant to be one of a fixed, known set of literals — like the discriminant in a discriminated union from the narrowing lesson.
const STATUSES = ["loading", "success", "error"] as const;
type Status = (typeof STATUSES)[number]; // "loading" | "success" | "error"
The union type is derived from the array, instead of being written out by hand twice — if STATUSES changes, Status changes with it automatically.
let count = 0 — no annotation written, but TypeScript infers count: number from the initializer.
let widens; const narrows primitives, but not arrays/objects by default. The mutability of the binding (let vs const) isn't the same question as the mutability of the contents — that's exactly what as const addresses for the contents.as const is a compile-time-only assertion. It changes nothing about runtime behavior — the array or object is exactly the same JavaScript value; only TypeScript's understanding of its type gets more precise.typeof STATUSES[number]) avoids writing the same list of literals twice — a very common source of drift, where an array of allowed values and a hand-written union type slowly disagree after one gets updated and the other doesn't."What does as const do?" is a quick, concrete way to check whether someone understands TypeScript's widening behavior at all — and the follow-up, deriving a union type from a const array instead of duplicating it, is a small pattern that shows real day-to-day TypeScript fluency.