An interface is another way to name an object type:

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

function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}

printCoord({ x: 100, y: 100 });

TypeScript only cares about structure: if the value has the right properties, it matches. That is why TypeScript is called a structurally typed system.

Interfaces vs type aliases

For object shapes they are very similar. You can often choose either. The important differences:

interface type
Extend extends Intersection (&)
Re-open / merge Yes : declare again to add fields No : duplicate name errors
Unions / primitives Prefer type Great for A | B, renaming primitives

Extending

interface Animal {
  name: string;
}

interface Bear extends Animal {
  honey: boolean;
}

const bear: Bear = { name: "Winnie", honey: true };
bear.name;
bear.honey;

With a type alias:

type Animal = {
  name: string;
};

type Bear = Animal & {
  honey: boolean;
};

Declaration merging

Interfaces can be reopened:

interface Window {
  title: string;
}

interface Window {
  ts: number;
}

// Window now has both title and ts

A type alias cannot do that : a second type Window = ... is an error.

Heuristic

Use interface for object shapes until you need a feature that only type provides (unions, mapped types, renaming a primitive). TypeScript will push you toward the other form when it matters.

interface User {
  id: number;
  name: string;
  email?: string;
}

interface Admin extends User {
  permissions: string[];
}

function isAdmin(user: User): user is Admin {
  return "permissions" in user;
}

(user is Admin is a type predicate : useful later when narrowing custom types.)

Practice

  1. Convert an inline { x: number; y: number } parameter to an interface Point.
  2. Extend it to interface Point3D extends Point { z: number }.
  3. Rewrite the same shapes with type and &, and note which form you prefer for your project.