Interfaces
TypeScript Programming
Interfaces
Interfaces are one of TypeScript's most powerful features for defining the shape of objects. An interface declares a contract that specifies which properties and methods an object must have, along with their types. Unlike type aliases (which we will cover next), interfaces are open — they can be extended and merged, making them ideal for defining public APIs and data models. Interfaces exist purely at compile time and generate no JavaScript code, so they add zero overhead to your runtime bundle.
Defining Interfaces
An interface declares a set of named properties with their types. Objects that match the interface must have all required properties with the correct types.
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
// Object must match the interface shape
const alice: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
isActive: true,
};
// Error: Property 'email' is missing
// const bob: User = { id: 2, name: "Bob", isActive: true };
// Error: Type 'string' is not assignable to type 'number'
// const charlie: User = { id: "three", name: "Charlie", email: "c@test.com", isActive: true };
Optional and Readonly Properties
Interfaces support optional properties (marked with ?) and readonly properties that cannot be changed after initialization.
interface Product {
readonly id: number;
name: string;
price: number;
description?: string; // optional
tags?: string[]; // optional
}
const laptop: Product = {
id: 101,
name: "MacBook Pro",
price: 2499,
tags: ["electronics", "computers"],
};
// laptop.id = 102; // Error: Cannot assign to 'id' because it is read-only
laptop.price = 2299; // OK — price is not readonly
Method Signatures
Interfaces can include method signatures that describe functions an object must implement.
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
multiply(a: number, b: number): number;
divide(a: number, b: number): number | null;
}
const calc: Calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => (b === 0 ? null : a / b),
};
console.log(calc.add(10, 5)); // 15
console.log(calc.divide(10, 0)); // null
Extending Interfaces
Interfaces can extend one or more other interfaces using the extends keyword, creating a hierarchy of types that inherit properties. This promotes reuse and keeps your type definitions DRY.
interface Timestamped {
createdAt: Date;
updatedAt: Date;
}
interface SoftDeletable {
deletedAt: Date | null;
}
// Extend multiple interfaces
interface BlogPost extends Timestamped, SoftDeletable {
id: number;
title: string;
body: string;
authorId: number;
published: boolean;
}
const post: BlogPost = {
id: 1,
title: "TypeScript Interfaces",
body: "Interfaces define object shapes...",
authorId: 42,
published: true,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
};
Implementing Interfaces in Classes
Classes can implement one or more interfaces using the implements keyword. The compiler verifies that the class provides all required properties and methods declared by the interface.
interface Serializable {
toJSON(): string;
}
interface Printable {
print(): void;
}
class Invoice implements Serializable, Printable {
constructor(
public id: number,
public amount: number,
public customer: string
) {}
toJSON(): string {
return JSON.stringify({
id: this.id,
amount: this.amount,
customer: this.customer,
});
}
print(): void {
console.log(`Invoice #${this.id}: $${this.amount} — ${this.customer}`);
}
}
const inv = new Invoice(1001, 250.0, "Acme Corp");
inv.print();
console.log(inv.toJSON());
Tip: Use interfaces for object shapes and public API contracts. They support declaration merging, which is useful for extending third-party type definitions. Use type aliases when you need unions, intersections, or mapped types.
Key Takeaways
- Interfaces define the shape of objects with typed properties and methods.
- Use
?for optional properties andreadonlyfor immutable properties. - Interfaces can extend other interfaces with
extendsfor type composition. - Classes implement interfaces with
implements, and the compiler verifies compliance. - Interfaces generate no runtime code — they are purely compile-time constructs.