Sostieni AppuntiFacili con una piccola donazione su PayPal
Dona con PayPalA literal is the textual representation of a value exactly as it is written in source code: 23 is a number literal, "Hello" is a string literal. In most languages a literal is only ever used to produce a value. TypeScript goes one step further and lets you use literals as types themselves.
let direction: "north" | "south" | "east" | "west";
direction = "north"; // ok
// direction = "hello"; // error: Type '"hello"' is not assignable to type '"north" | "south" | "east" | "west"'
The type of direction is not “any string” — it is exactly one of four specific strings. This is called a union of literal types, and it turns what used to be a runtime bug (passing an unexpected string) into a compile-time error.
let responseCode: 200 | 404 | 201;
responseCode = 201;
console.log(responseCode);
The same idea works with numbers, booleans, or any combination of them. Literal unions are extremely common for representing a fixed, known set of options — HTTP status codes, UI variants, days of the week — without introducing a separate type declaration.
TIP
Literal types are what makes autocomplete so useful in TypeScript: your editor can suggest exactly "north" | "south" | "east" | "west" instead of “any string”.
Enums let you define a set of named constants (called members or enumerators), each associated with a value. Unlike a literal union, an enum is a real construct: it exists as a value at runtime, not just as a compile-time type.
enum Size {
Small,
Medium,
Large
}
var size: Size = 0; // Small
if (size === Size.Small) {
// ...
}
By default, enum members are numbered starting from 0, incrementing by one for each subsequent member — Small is 0, Medium is 1, Large is 2. You can also assign your own starting number, and TypeScript will auto-increment from there:
enum StatusCode {
Ok = 200,
NotFound = 404,
ServerError = 500
}
A subtle downside of numeric enums is that they are bidirectional: Size[0] gives you back "Small", and Size.Small gives you 0. This is convenient for debugging, but it also means a plain number like 2 can be assigned where a Size is expected, even if it wasn’t meant to represent that enum.
String enums fix that ambiguity: each member is a specific string, so there’s no accidental numeric coercion, and the value you see while debugging (in logs, in the debugger) is meaningful instead of an opaque index.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
console.log(Direction.Up); // "UP"
Unlike numeric enums, string enum members do not auto-increment — every member must be explicitly initialized with its own string.
enum Description {
SmallText = "this is some sub text to read",
}
console.log(Description.SmallText);
Prefixing an enum with const tells the compiler to fully inline its values wherever they are used, and to avoid generating a runtime object for the enum at all:
const enum Level {
Low,
Medium,
High
}
let l = Level.Medium; // compiles to: let l = 1;
const enum produces smaller, faster output when you don’t need to iterate over the enum’s members or look values up by reverse mapping — but be aware it can’t be used with certain build tools that only transpile files in isolation (like Babel), since it requires whole-program type information to inline correctly.
WARNING
const enum is not supported by all bundlers/transpilers. If you hit strange build issues, check whether your toolchain (isolated-modules transpilation especially) supports it before reaching for it.
INFO
Food for thought: many modern TypeScript style guides — and even members of the TypeScript team — suggest preferring a union of string literals (type Direction = "UP" | "DOWN" | "LEFT" | "RIGHT") over enums for simple cases. Literal unions have no runtime footprint, serialize naturally to JSON, and avoid the numeric-enum pitfalls above. Enums remain a legitimate, widely used tool — just know the trade-off exists.
| Literal union | Enum | |
|---|---|---|
| Exists at runtime | No (type-only) | Yes (object, unless const enum) |
| Bidirectional numeric mapping | No | Numeric enums only |
| Extra syntax needed | No | Yes (enum keyword) |
| Good for simple fixed value sets | Yes | Yes |
| Good when you need to iterate members / attach behavior | Less convenient | Yes |
Both approaches solve the same underlying problem — restricting a variable to a fixed, known set of values — and the right choice usually comes down to whether you need the enum to exist as a real JavaScript value at runtime.
What is a literal type in TypeScript?
What value does `Size.Small` have by default in `enum Size { Small, Medium, Large }`?
What is a key difference between numeric and string enums?
What does prefixing an enum with `const` do?
Why can numeric enums be considered less type-safe than string enums?
Which of these is a valid literal union type declaration?
What is a commonly cited advantage of a literal union over an enum for simple fixed value sets?
Scenario: You are building the settings panel of an app and need to represent a fixed set of theme options.
Task:
Theme with values "light", "dark", and "system".applyTheme(theme: Theme): void that logs a different message per theme using an if/else or switch.Theme and observe the compiler error.ThemeEnum and compare the two approaches in a short comment.Learning objective: understand literal union types, and compare them directly against string enums for the same use case.
Scenario: You need to model HTTP-like response statuses for a small mock API layer.
Task:
HttpStatus with members Ok = 200, NotFound = 404, ServerError = 500.describeStatus(code: HttpStatus): string returning a human-readable description for each status.HttpStatus[200].const enum and explain, in a comment, what changes about the generated output.Learning objective: practice defining and consuming numeric enums, understand reverse mapping, and see the effect of const enum.