Skip to content
Theory · Interfaces and structural typing · lesson 11 of 12

type or interface

A workshop keeps its lock descriptions in two ways. Some go into a ring binder: it holds only drawings, but a new sheet can be added later, and the binder keeps all the sheets together. Others get a label with a name. A label can be stuck on anything — a drawing, a box marked "this lock or that one", a pair of items. But a second label with the same name cannot be stuck on.

interface and type work the same way: an interface is the binder, a type alias is the label. Both describe an object's shape, and most of the time there is no difference between them. They differ in two places, and interviewers like to ask about them: "How does type differ from interface?"

The same for objects

Let's describe one shape both ways:

TypeScript
type PointType = {
    x: number;
    y: number;
};

interface PointShape {
    x: number;
    y: number;
}

const a: PointType = { x: 1, y: 2 };
const b: PointShape = a;
console.log(b.x + b.y); // 3

Let's go through the example:

  1. PointType and PointShape describe the same shape: numbers x and y.
  2. a is declared as a PointType, and the line const b: PointShape = a; passes anyway. Types are compared by shape, and the shapes match.
  3. The program prints 3.

For an object's shape, it does not matter which way it was written:

  • tsc checks a literal the same way: a missing property is TS2741, an extra one is TS2353;
  • ? and readonly work in both;
  • both are erased and do not reach the JavaScript;
  • a shape is extended with extends on an interface and with & on an alias.

Only type: unions and tuples

A label can be stuck on anything, while the binder holds only drawings. An alias names any type:

TypeScript
type Id = string | number;
type Pair = [string, number];

const id: Id = 42;
const entry: Pair = ["apple", 5];
console.log(id, entry[0]); // 42 apple

An interface, on the other hand, describes only an object's shape. You cannot write the union string | number or a tuple with it: tsc cannot even parse interface Id = string | number; and answers error TS1005: '{' expected.

Only interface: declaration merging

Two interface declarations with one name do not clash; they add up to one:

TypeScript
interface Config {
    port: number;
}

interface Config {
    host: string;
}

const config: Config = { port: 8080, host: "localhost" };
console.log(`${config.host}:${config.port}`); // localhost:8080

This is declaration merging: Config now has two required properties, port and host. tsc rejects an object without host — the property is missing, error TS2741. Merging is how other people's interfaces get extended. For example, in a file with no import or export, you add your own property to Window, the description of the browser window, by declaring another interface Window.

Which to choose

There is no strict rule, and a team picks one style. A common agreement is:

  • an object's shape is an interface: it can be extended with extends and added to by merging;
  • unions, tuples and sets of literals are a type, since an interface cannot describe them.

What matters most is that one project describes object shapes the same way everywhere. In this course, from this theme on, object shapes are interfaces.

Common mistake

The goal was to add a property to an alias with a second declaration, as with an interface:

TypeScript
type User = { name: string };
type User = { age: number }; // ✗
TEXT
error TS2300: Duplicate identifier 'User'.

The message means "the name User is declared twice", and tsc puts it on both lines. Aliases do not merge: there is no second label with the same name. If you need a type with both properties, declare an interface twice or build a new type with &: type Person = User & { age: number };.

Key points

  • For an object's shape, type and interface are almost the same: the same checks, the same ? and readonly, and both are erased.
  • Unions and tuples can be named only with type.
  • Two interfaces with one name merge into one, while two types with one name are error TS2300.
  • A team picks one style; a common rule is interface for objects and type for the rest.