Literal Types & const Assertions

A literal type restricts a value to one exact constant — like the type "active" or 42 — and combined into unions they give you a precise, enum-like set of allowed values.

Learn Literal Types & const Assertions in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

A plain string type is like a blank text box — you can type anything . A literal union is like a dropdown menu : the field still holds a string, but only the specific options on the list are valid. "S" | "M" | "L" is the dropdown; "XXL" simply isn't on it, so the compiler rejects it before your program ever runs.

1. Literal Types: One Exact Value

A literal type is a type made of a single constant value. "active" as a type accepts only the string "active" ; 6 as a type accepts only the number 6 . By themselves they're rarely useful — their power shows when you join several with | to form a fixed set, like "north" | "south" | "east" | "west" .

2. Widening vs Narrowing

When you initialise a variable, TypeScript decides between the broad type and the exact literal. A let can be reassigned, so TypeScript widens let theme = "dark" to string . A const never changes, so it narrows const fixed = "dark" to the literal type "dark" . Understanding this explains a lot of "why is my type just string ?" surprises.

3. as const Assertions

An as const assertion tells TypeScript: "infer the narrowest possible type and make it readonly ." Object fields keep their literal types instead of widening, arrays become readonly tuples of literals, and you can derive a union directly from data: turns ["sm","md","lg"] as const into "sm" | "md" | "lg" — one source of truth for both the values and the type.

🎯 Your Turn

Model a traffic light whose colour is one of three literals. Fill in the two blanks marked ___ so the cycle goes red → green → amber → red, then run it.

No blanks this time — just a brief and a starting outline. Build the literal-typed state machine yourself, run it, and check your output against the example in the comments.

Practice quiz

What is a literal type?

  • A type that accepts any string
  • A runtime literal value
  • A type whose only value is one exact constant
  • A union of all numbers

Answer: A type whose only value is one exact constant. A literal type like "active" or 42 accepts only that one exact value.

What does a union of literals like "sm" | "md" | "lg" give you?

  • A precise, enum-like set of allowed values
  • A random value
  • Any string
  • A tuple

Answer: A precise, enum-like set of allowed values. Joining literals with | creates a fixed set of allowed options, like a dropdown menu.

What type does let theme = "dark" infer?

  • "dark"
  • any
  • literal
  • string

Answer: string. A let can be reassigned, so TypeScript widens it to string.

What type does const fixed = "dark" infer?

  • string
  • "dark"
  • any
  • char

Answer: "dark". A const never changes, so it narrows to the literal type "dark".

What does as const do to a value?

  • Freezes it into its narrowest readonly literal form
  • Makes it mutable
  • Converts it to a string
  • Removes its type

Answer: Freezes it into its narrowest readonly literal form. as const infers the narrowest possible type and makes the value readonly.

For const SIZES = ["sm", "md", "lg"] as const, what is typeof SIZES[number]?

typeof ARR[number] on an as const array yields the union of its element literals.

Why might passing a let string to a function expecting "dark" | "light" fail?

  • The value is wrong
  • The let was widened to string, which is not assignable to the literal union
  • Functions cannot take strings
  • let is not allowed in TypeScript

Answer: The let was widened to string, which is not assignable to the literal union. Even with a correct value, a widened string is not assignable to the narrower literal union.

Which is a valid numeric literal union type?

  • type Dice = number(6)
  • type Dice = int<6>

Numeric literals combine with | just like strings: 1 | 2 | 3 | 4 | 5 | 6.

Compared with an enum, a literal union of strings is generally...

  • Heavier and needs runtime code
  • Lighter, needs no runtime object, and is type-safe
  • Unsafe
  • Only for numbers

Answer: Lighter, needs no runtime object, and is type-safe. A literal union is lighter and needs no runtime object; most teams prefer it for string sets.

Why declare options as const OPTS = [...] as const and derive the type with typeof OPTS[number]?

  • It runs faster
  • It disables type checking
  • It keeps a single source of truth so the array and type stay in sync
  • It is required syntax

Answer: It keeps a single source of truth so the array and type stay in sync. Deriving the type from the array means changing the array updates the type automatically.