TypeScript Aliases and Interfaces

Type aliases and interfaces are two ways to name and reuse type shapes in TypeScript, and understanding their differences helps you choose the right tool for each situation.

Declaring a Type Alias

A type alias, declared with the type keyword, gives a name to any type: an object shape, a union, a tuple, or even a primitive. It is the more general and flexible of the two mechanisms.

Type Alias Basics

type UserId = string;

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

type Status = "active" | "inactive" | "banned";

const id: UserId = "user_123";
const origin: Point = { x: 0, y: 0 };
const status: Status = "active";

Declaring an Interface

An interface, declared with the interface keyword, describes the shape of an object or a class contract. It reads similarly to a type alias for object shapes but is limited to describing object-like structures.

Interface Basics

interface Point3D {
  x: number;
  y: number;
  z: number;
}

function distanceFromOrigin(point: Point3D): number {
  return Math.sqrt(point.x ** 2 + point.y ** 2 + point.z ** 2);
}

const p: Point3D = { x: 3, y: 4, z: 0 };
console.log(distanceFromOrigin(p)); // 5

Extending and Merging

Interfaces can be extended with the extends keyword and support declaration merging, meaning two interfaces with the same name in the same scope automatically combine their members. Type aliases use intersections (&) to combine shapes instead, and cannot be merged by re-declaring them.

Extending an Interface vs Intersecting a Type

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

type AnimalT = { name: string };
type DogT = AnimalT & { breed: string };

const dog: Dog = { name: "Rex", breed: "Labrador" };
const dogT: DogT = { name: "Milo", breed: "Beagle" };
Note: Declaration merging means two separate 'interface Window { ... }' blocks in different files automatically combine into one — this is how libraries safely add properties to global types.

type vs interface

Capabilitytypeinterface
Object shapesYesYes
Unions and tuplesYesNo
Declaration mergingNoYes
Extending another shapeVia & intersectionVia extends keyword
Implementing in a classYes (object-like types)Yes
Note: Because interfaces merge automatically, accidentally declaring the same interface name twice in a large codebase can silently combine unrelated definitions — use type aliases when you want a hard guarantee against merging.

Realistic Use Case: Public API Shapes

A common convention is to use interfaces for the public shapes of objects and classes that consumers of a library might extend, and type aliases for unions, tuples, and one-off compositions.

Combining Both in Practice

interface ApiError {
  code: number;
  message: string;
}

type ApiResult<T> = { success: true; data: T } | { success: false; error: ApiError };

function handleResult(result: ApiResult<string>) {
  if (result.success) {
    console.log("Data:", result.data);
  } else {
    console.log("Error:", result.error.message);
  }
}
  • Use interface for object shapes that libraries or consumers might extend or merge.
  • Use type for unions, tuples, mapped types, and other non-object compositions.
  • Both support generics and can be used interchangeably for plain object shapes.
  • Neither one has a runtime representation — both disappear when compiled to JavaScript.

Exercise: TypeScript Aliases & Interfaces

Which statement about type aliases is accurate?