let, const and literal types
The helper labels jars by what is inside. But they label the same grain differently when the jars differ. An ordinary jar is opened every day: today it holds 200 grams of buckwheat, tomorrow 150. Its label says the general "Buckwheat", not the exact weight. A sealed jar will never be opened again, so its label can be exact: "Buckwheat, 200 g".
In TypeScript the ordinary jar is let, and the sealed one is const:
let count = 5;
const limit = 5;
console.log(count + limit); // 10Both variables hold the same value, yet their types differ. Hover over them: over count the hover shows let count: number, and over limit it shows const limit: 5.
A literal type
The 5 in the hover over limit is not a value but a type. A literal type is a type with exactly one value: a variable of type 5 can hold only the number 5. Such types are named after their value: 5, "red", true.
For a constant this is an exact description. The value of limit will never change, and TypeScript writes everything it knows about it on the label.
Why let widens the type
That does not work for let. If let count = 5 got the type 5, the line count = 6 would become an error, and the counter could never go up. So TypeScript takes the general type the value belongs to: number instead of 5, string instead of "red". This replacement is called type widening.
Widening also happens when a constant's value goes into a new let variable. const brand = "red" has the type "red", but let copy = brand gets string again: the new variable may change too.
This is where the jar analogy ends. In a kitchen you can see whether a jar gets opened, but TypeScript does not track whether a variable really changes. Even if count never gets a new value, its type stays number: the word in the declaration, let or const, decides.
Press "Next step" and watch the editor's hover: let gets a general type, const an exact one, and an object's property a general one again.
1let count = 5;editor hovercount: number2const limit = 5;3let color = "red";4const brand = "red";5let copy = brand;6const point = { x: 5 };7point.x = 7;
A let gets the general type number: count may later hold another number.
The type depends not only on the value but also on whether it can change: let gets a general type, const a literal one.
An object in a const
const point = { x: 5 };
point.x = 7;
console.log(point.x); // 7const fixes only the name point: another object cannot be written into it. But the property x can change, so TypeScript gives it a general type. point gets the type { x: number; }, not { x: 5; }. This is how TypeScript writes an object's type: the braces list the properties and their types, x: number meaning "the property x has the type number".
What a literal type is for
An exact type helps the compiler find code that makes no sense:
const limit = 5;
if (limit === 6) { // ✗
console.log("шесть");
}error TS2367: This comparison appears to be unintentional because the types '5' and '6' have no overlap.The message means: "the comparison looks accidental: the types 5 and 6 share no values". TypeScript knows that limit always holds 5, so equality with 6 can never be true. With let limit = 5 there would be no error: the type number allows 6 too.
Interviewers like to ask why let and const with the same value get different types. The answer is about change: a let may get a new value, so its type is widened, and a const may not, so its type stays literal.
Key points
- A literal type is a type with one value:
5,"red",true. const limit = 5gets the literal type5: a constant's value will not change.let count = 5getsnumber: TypeScript widens the type because the variable may change.- The properties of an object in a
constget general types:const point = { x: 5 }is{ x: number; }.