Skip to content
Type Aliases vs Interfaces
step 1/7

Reading — step 1 of 7

Learn

~3 min readType System Essentials

Both type and interface describe the shape of values. They overlap heavily — but each has cases where it shines. The TypeScript Handbook's recommendation: use whichever fits, lean toward interface for object shapes that may extend.

Interface

interface User {
    name: string;
    age: number;
    greet(): string;
}

const ada: User = {
    name: 'Ada',
    age: 36,
    greet() { return `Hi, I'm ${this.name}`; }
};

Inheritance with extends:

interface Animal {
    name: string;
}

interface Dog extends Animal {
    breed: string;
}

const rex: Dog = { name: 'Rex', breed: 'Husky' };    // both fields required

Class implementation:

class Logger implements Loggable {
    log(msg: string) { console.log(msg); }
}

Type alias

type User = {
    name: string;
    age: number;
    greet(): string;
};

Looks similar — but type can describe MORE:

type StringOrNumber = string | number;       // unions — interface CANNOT
type Pair<T> = [T, T];                        // tuples
type Callback = (x: number) => void;          // function shorthand
type Brand = string & { __brand: 'BrandedString' };   // intersections
type Tree<T> = { value: T; children: Tree<T>[] };     // recursive

Composition with intersection:

type Animal = { name: string };
type Dog = Animal & { breed: string };

const rex: Dog = { name: 'Rex', breed: 'Husky' };

Similar effect to extends — but using intersection.

The big differences

Interfaces are open — declaration merging

Multiple interface declarations of the same name automatically merge:

interface Window {
    customField: string;
}

// Elsewhere — usually in a .d.ts file
interface Window {
    anotherField: number;
}

// window now has both
window.customField;
window.anotherField;

This is HOW you augment global types like Window or Express.Request. Type aliases CANNOT merge — duplicate type Foo = ... is an error.

Types are closed — but more flexible

type Status = 'on' | 'off';                  // ✓
interface Status = 'on' | 'off';              // ✗ — interface can't be a union

type Coords = [number, number];              // ✓
interface Coords { 0: number; 1: number; length: 2 }  // awkward

Performance (in big codebases)

For pure object shapes that extend, interfaces are slightly faster for the type checker — TypeScript caches their resolution. With 100k+ types, it adds up.

For everyday code: pick whatever reads better.

When to use which

Use interface for:

  • Public APIs you want consumers to extend or augment
  • Class contracts (implements MyInterface)
  • Object shapes where you might add fields later
  • Anything in .d.ts files for declaration merging

Use type for:

  • Unions, intersections, tuples, function types
  • Mapped/conditional/template-literal types
  • Branded types
  • Anything that needs the "type-only" expressiveness

Common mistakes

  • Trying to declare-merge a type alias — error. Use interface.
  • Using interface for unionsinterface X = A | B is a syntax error. Use type.
  • Naming interfaces IUser — historic C#-ism, no longer recommended in modern TS. Just User.
  • Forgetting that extends with interface vs & with type behave slightly differently for conflicting fields — interface extends errors on conflict; intersection silently produces never.
  • Mixing types and interfaces inconsistently in one codebase — pick conventions and stick to them.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…