Skip to content
Laviyx Learn

Try

TypeScript course menu

TypeScript Interfaces: Defining Object Shapes, Extending and Merging

Learn TypeScript interfaces: describing object shapes, optional and readonly properties, methods, extending interfaces, declaration merging, implementing interfaces in classes, and the practical differences between interface and type.

Intermediate8 min readUpdated
On this page

An interface names the shape of an object. Interfaces are the traditional way to describe objects in TypeScript and are used widely in libraries and codebases.

Declaring an interface#

TypeScript
interface Product {
  readonly id: string;
  name: string;
  price: number;
  tags?: string[];
  describe(): string;
}

const pen: Product = {
  id: "P-1",
  name: "Gel pen",
  price: 20,
  describe() {
    return `${this.name} costs ₹${this.price}`;
  },
};

console.log(pen.describe());
Output
Gel pen costs ₹20

The body uses the same syntax as object types: optional ?, readonly, and methods.

Extending interfaces#

extends creates an interface that has all the members of another, plus its own:

TypeScript
interface Person {
  name: string;
  email: string;
}

interface Employee extends Person {
  employeeId: number;
  department: string;
}

interface Manager extends Employee {
  reports: Employee[];
}

const ravi: Employee = { name: "Ravi", email: "ravi@example.com", employeeId: 7, department: "Design" };
const asha: Manager = {
  name: "Asha",
  email: "asha@example.com",
  employeeId: 1,
  department: "Engineering",
  reports: [ravi],
};

console.log(`${asha.name} manages ${asha.reports.map((r) => r.name).join(", ")}`);
Output
Asha manages Ravi

An interface can extend several others at once: interface Admin extends Employee, Auditor {}.

Declaration merging#

Two interface declarations with the same name in the same scope merge into one:

TypeScript
interface Settings {
  theme: string;
}
interface Settings {
  fontSize: number;
}

const s: Settings = { theme: "dark", fontSize: 16 };
console.log(s);
Output
{ theme: 'dark', fontSize: 16 }

This is mainly useful for adding properties to types defined elsewhere, such as extending a library's types or adding a property to the global Window interface. Within your own code, it can be surprising, which is one reason some teams prefer type.

Classes can implement interfaces#

A class can promise to match an interface with implements. The compiler checks that it does:

TypeScript
interface Shape {
  area(): number;
  name: string;
}

class Square implements Shape {
  name = "square";
  constructor(private side: number) {}
}
Compiler error
error TS2420: Class 'Square' incorrectly implements interface 'Shape'.

Adding an area() method fixes it. This is covered in Abstract Classes and implements.

Interfaces for functions and indexable objects#

Interfaces can also describe callable objects and dictionaries, although type aliases are often simpler for those:

TypeScript
interface Comparator {
  (a: number, b: number): number;
}

interface PriceList {
  [item: string]: number;
}

const ascending: Comparator = (a, b) => a - b;
const prices: PriceList = { tea: 20, coffee: 35 };

console.log([3, 1, 2].sort(ascending), prices.coffee);
Output
[ 1, 2, 3 ] 35

interface or type?#

interfacetype
Describes objectsYesYes
Unions, tuples, primitives, function typesNo (functions only with call signatures)Yes
ExtendingextendsIntersections &
Declaration mergingYesNo (a duplicate name is an error)
Error messagesShow the interface nameSometimes show the expanded shape

Both are fine for object shapes, and teams pick a convention. A common one:

  • interface for object shapes, especially public ones that others might extend;
  • type for unions, tuples, function types and anything composed from other types.

Whatever you choose, be consistent within a project.

Practice#

Practice exercise

Create an interface Vehicle with make, model, year and a method age(currentYear: number): number. Extend it into ElectricVehicle with batteryKwh and rangeKm. Create one electric vehicle and print its age in 2026 and its range per kWh to one decimal place.

Show solution
TypeScript
interface Vehicle {
  make: string;
  model: string;
  year: number;
  age(currentYear: number): number;
}

interface ElectricVehicle extends Vehicle {
  batteryKwh: number;
  rangeKm: number;
}

const ev: ElectricVehicle = {
  make: "Example Motors",
  model: "City EV",
  year: 2022,
  batteryKwh: 30,
  rangeKm: 315,
  age(currentYear) {
    return currentYear - this.year;
  },
};

console.log(`${ev.model} is ${ev.age(2026)} years old`);
console.log((ev.rangeKm / ev.batteryKwh).toFixed(1), "km per kWh");
Output
City EV is 4 years old
10.5 km per kWh

Key takeaway#

Finished this lesson?