Type Aliases and Literal Types
Naming a type with type, and narrowing a primitive down to its exact allowed values.
3 min read
type aliases and literal types have both come up in earlier lessons in passing — this one covers them directly, as tools in their own right rather than as a comparison against interface.
Type aliases: naming any type
A type alias gives a name to any type expression, so you can reuse it instead of repeating it:
type ID = string | number;
type Point = { x: number; y: number };
type Callback = (error: Error | null, result?: string) => void;
function findUser(id: ID) { /* ... */ }Unlike interface, which only names object shapes, type can name a union, a tuple, a function signature, or a primitive alias — anything expressible in TypeScript's type syntax.
Literal types: a value as its own type
TypeScript can treat a specific literal value — not just its general type — as a type on its own:
let direction: "up" = "up";
direction = "down"; // Type '"down"' is not assignable to type '"up"'On their own, literal types are rarely useful for a single value — you'd normally just use string. Their real value comes from combining several into a union, as covered in an earlier lesson:
type Direction = "up" | "down" | "left" | "right";
function move(direction: Direction) { /* ... */ }
move("up"); // fine
move("diagonal"); // Argument of type '"diagonal"' is not assignable to type 'Direction'This is the pattern behind modeling a fixed, known set of string values — form field states, HTTP methods, theme names — with full autocomplete and compile-time checking, without needing an enum.
Numeric and boolean literal types
The same idea applies beyond strings:
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
type Toggle = true | false; // equivalent to `boolean`, written out explicitlyconst assertions: inferring the literal type instead of the general one
By default, TypeScript infers a let or const variable's type as the general type, not the literal:
let status = "active"; // inferred as string, not "active"
const status2 = "active"; // inferred as "active" (const can't be reassigned)
let config = { role: "admin" }; // role inferred as string, not "admin"as const forces TypeScript to infer the narrowest possible literal type throughout an object or array, instead of widening it to the general type:
const config = { role: "admin" } as const;
// role is inferred as "admin", not string -- and the whole object becomes readonlyThis is especially useful for a fixed configuration object or a tuple you want treated as its exact literal shape, rather than a general, widened one.
When to reach for a type alias vs an interface
As covered in the earlier comparison lesson: prefer interface for object shapes, especially ones meant to be extended or merged, and reach for type for unions, tuples, and function types — anything an interface can't express directly.
The final lesson before wrapping up practical TypeScript usage covers something every real project runs into: getting types for a third-party package that doesn't ship its own.