A literal type has exactly one value: "GET", 42, true. On their own they are rarely useful; in a union ("GET" | "POST") they model a closed set of options the compiler can check.
Widening. When TypeScript infers a type from a literal, it decides whether the binding could later hold a different value:
const method = "GET"has type"GET": it can never change.let method = "GET"has typestring: it can be reassigned, so the literal is widened.- Properties of an object literal are mutable, so
{ method: "GET" }hasmethod: string. This is the classic bug: passingreq.methodto a parameter typed"GET" | "POST"fails.
as const (a const assertion, TS 3.4) asks for the narrowest type: literals are not widened, object literal properties become readonly, and array literals become readonly tuples. It applies only directly to literal expressions (or enum members), and it only affects types: it emits no code and doesn't freeze anything, and nested values you reference from outside can still be mutated.
Common uses:
- Keeping a config object's literal values.
- Deriving a union from a list:
(typeof dirs)[number]gives"up" | "down". - An enum-like object whose values form a union.
Related tools:
satisfies(TS 4.9) checks a value against a type without widening it;as const satisfies Tcombines both.<const T>type parameters (TS 5.0) infer literal types for arguments written inline.unique symbol(TS 2.7): the type of aconstsymbol, distinct fromsymboland from every other unique symbol.