CodeOath
← All posts
TypeScript75 min total · 21 parts

TypeScript Fundamentals: Types, Interfaces, Generics, and Why It Catches Bugs Before Runtime

Contents — Part 8 of 21: Literal Types and as const
Part 8 of 21 · ~1 min

Literal Types and as const

A literal type narrows a primitive to one specific value rather than the whole category — "loading" as a type, not just string.

let status: "loading" | "success" | "error";
status = "loading"; // fine
status = "done";     // Error: Type '"done"' is not assignable

Plain variable declarations widen literals back to their general type by default, which is why as const matters:

let a = "hello";        // inferred as string (widened)
const b = "hello";      // inferred as "hello" (a const primitive can't change, so TS keeps it literal)

const config = { role: "admin" };
// config.role is inferred as string, NOT "admin" — object properties widen even with const,
// because the property itself could still be reassigned (config.role = "guest" is legal)

const config2 = { role: "admin" } as const;
// config2.role is now the literal type "admin", and config2 itself is deeply readonly

as const is the practical fix any time you want an object or array literal to keep its exact literal types instead of widening — extremely common when defining a fixed set of options, a Redux-style action, or an array meant to be treated as a tuple.

const point = [3, 4] as const; // type: readonly [3, 4] — a tuple, not number[]