Type Aliases

Giving any type — not just object shapes — a reusable name.

What is it?

Interfaces are great for naming object shapes, but not every type you want to reuse is an object. Maybe it's a specific set of allowed strings, like "pending" | "shipped" | "delivered". Maybe it's a function's signature. Maybe it's just a shorthand for a long, awkward-to-repeat type. Writing that out in full every time it's needed is just as repetitive as repeating an object shape.

A type alias, written with the type keyword, gives any type a name — an object shape, a union of specific values, a tuple, a function signature, anything. Once named, you refer to it by that name everywhere instead of repeating the full definition.

Explain like I'm 10

A type alias is like giving a nickname to a long phrase you keep repeating. Instead of writing "the delivery status, which is one of pending, shipped, or delivered" every single time, you agree to just say "OrderStatus" and everyone knows exactly what that means.

Examples

Naming a union of specific values

type OrderStatus = "pending" | "shipped" | "delivered";

let status: OrderStatus = "shipped";

status = "cancelled";
// Error: Type '"cancelled"' is not assignable to type 'OrderStatus'.

OrderStatus isn't an object shape at all — it's a fixed set of allowed string values. Type aliases can name this kind of type, which interfaces cannot.

A type alias for an object shape (like an interface)

type Point = {
  x: number;
  y: number;
};

function distanceFromOrigin(p: Point): number {
  return Math.sqrt(p.x ** 2 + p.y ** 2);
}

// interface vs type, for an object shape, are nearly interchangeable:
interface PointInterface {
  x: number;
  y: number;
}

For plain object shapes, type and interface do almost the same job. The practical differences show up in more advanced cases: interfaces can be re-opened later to add more properties (declaration merging), and type aliases can describe unions, tuples, and other non-object types that interfaces can't.

How it works

Like an interface, a type alias exists purely at compile time — it's a label the compiler substitutes in wherever it's used when checking your code, and it leaves no trace in the compiled JavaScript. The difference from an interface is what it's allowed to name: an interface can only describe object shapes, while a type alias can name literally any type — including unions, tuples, function signatures, and primitives — as well as object shapes.

Why does it exist?

Object shapes aren't the only kind of type worth reusing — restricted sets of string values, function signatures, and combinations of other types all benefit from having a single readable name instead of being repeated or inlined everywhere. Type aliases fill that gap for anything an interface can't express.

When to use it

Reach for a type alias when you're naming a union (like a fixed set of allowed strings), a tuple, a function signature, or any type that isn't strictly "an object with these properties." It's also fine to use for plain object shapes if your team's convention prefers type over interface for consistency.

When not to use it

If you're describing an object shape that might need to be extended later by merging in more properties from elsewhere (common in libraries), an interface supports that (declaration merging) and a type alias does not. For everyday object shapes without that need, either works.

Common mistakes

  • Thinking type and interface are totally interchangeable — type aliases can express unions and other non-object types that interfaces cannot.

  • Trying to "reopen" a type alias later to add more properties, the way you can with an interface — type aliases can't be merged like that.

  • Naming a type alias the same as an existing variable or another type in the same scope, causing a naming collision.

Practice exercises

  1. Easy:

    Create a type alias Direction that can only be "up", "down", "left", or "right", and declare a variable of that type.

  2. Medium:

    Create a type alias Coordinate for a [number, number] tuple, and write a function move(point: Coordinate, direction: Direction): Coordinate that returns a new coordinate.

  3. Hard:

    Write a type alias for a function signature, Comparator, that takes two numbers and returns a number, and write a function sortNumbers(nums: number[], compare: Comparator): number[] that uses it.

Interview questions

What can a type alias describe that an interface cannot?

A type alias can name unions, tuples, function signatures, and primitive types — anything, not just object shapes, which is all an interface can describe.

What is declaration merging, and which of `type` or `interface` supports it?

Declaration merging is the ability to declare the same named shape more than once and have TypeScript combine the declarations into one. Only interface supports this; a type alias with the same name declared twice is an error.

For a simple object shape, does it matter whether you use `type` or `interface`?

In most everyday cases, no — both check objects the same way. The choice mostly comes down to team convention, unless you specifically need a feature only one of them supports.

Does writing `type ID = string;` create a genuinely new type distinct from `string`?

No — it just creates an alternate name for the exact same type. Anywhere a string is expected, an ID works and vice versa; TypeScript treats them as fully interchangeable, unlike some languages' "branded" or "nominal" type features.

Given `type ID = string | number; let id: ID = "abc123"; id = 42;`, does the second assignment compile?

Yes — 42 is a number, which is one of the two types the union ID allows. Assigning id = true; would fail, though, since boolean isn't part of the union.

Can a type alias reference itself, like `type Tree = { value: number; children: Tree[] };`?

Yes, for object shapes — this is a recursive type alias, and TypeScript resolves it lazily rather than trying to expand it infinitely, which is exactly what lets you model nested structures like trees or JSON with a single named type.

What happens if you declare `type Status = "active" | "inactive";` twice in the same scope?

It's a compile error — unlike interface, a type alias cannot be declared more than once with the same name; there's no merging mechanism for type aliases.

How would you type a variable that holds a function signature, like a comparator used for sorting?

With a type alias for the function shape: type Comparator = (a: number, b: number) => number;. Any function assigned to a variable of that type is then checked against exactly that parameter and return signature.

Given `type Status = "active" | "inactive"; function setStatus(s: Status) {}`, why does `setStatus("Active")` fail to compile?

The union Status only allows the exact literal strings "active" and "inactive" — string literal types are case-sensitive, so "Active" isn't one of the allowed values, even though it looks similar.

How would you express "a `User` extended with an extra `permissions` field" using type aliases instead of `interface extends`?

With an intersection: type Admin = User & { permissions: string[] };. The & combines both shapes into one type that requires everything from User plus the new field — it's the type-alias equivalent of extends, just via a different operator.

You need to model a value that's always either a `User` object or a `Guest` object, distinguished by a shared `kind` field. Would you reach for `interface` or `type` here, and why?

A type alias, since the overall value is a union (User | Guest) — interfaces can't directly express "one of these two shapes," only a single object shape. Each branch of the union could still individually be an interface; only the union itself needs type.

Why can a type alias name "literally any type," while an interface is limited to object shapes?

A type alias is just a name bound to a type expression, and that expression can be a union, a tuple, a function signature, a primitive, or an object shape — an interface's syntax, by contrast, is specifically built to declare a set of named properties, so it has no way to represent something like a union of strings.

If you write `type Point = { x: number; y: number };` and separately `interface PointInterface { x: number; y: number }`, are values typed as one assignable to the other?

Yes — because TypeScript checks structurally, both describe the identical shape, so a Point value is assignable wherever a PointInterface is expected and vice versa, regardless of which one was used to declare it.

What's the main practical trade-off a team gives up by adopting "always use `type`, never `interface`" as a blanket style rule?

They lose declaration merging — the ability to reopen an already-declared shape and add more properties to it later, which some patterns, especially in library type definitions, rely on. For most everyday application code that trade-off rarely matters.

Can a tuple type be given a name with a type alias, and why would you want that?

Yes — for example type Coordinate = [number, number];. Naming it means every function that takes or returns a coordinate pair can refer to Coordinate instead of repeating [number, number], and if the shape ever needs to change, there's one definition to update.

Why can't an interface be used to name a type like `type Direction = "up" | "down" | "left" | "right";`?

An interface's syntax only supports declaring named properties on an object — there's no way to write "this interface is one of these four exact strings" using interface syntax, since an interface isn't describing a value directly, it's describing an object's shape.

Naming a type alias the same as an existing variable in the same scope — is that allowed?

It's allowed in the sense that types and values live in separate namespaces in TypeScript, so type User = {...} and let User = ... can technically coexist — but it's confusing in practice and best avoided, and naming collisions between two types with the same name are still errors.