Glossary

Interface

An interface is a contract that defines the shape of an object (its properties and method signatures) without specifying an implementation. Any object or class that satisfies the interface can be used wherever that interface is expected.

Explanation

In TypeScript, an interface declaration defines the structure that an object must have: property names, their types, and method signatures. TypeScript uses structural typing (duck typing): an object satisfies an interface if it has all the required properties with compatible types — you don't need an explicit implements declaration. This is the "if it has the right shape, it fits" approach. Interfaces serve as contracts in two contexts: defining the shape of data (interface User { id: number; email: string; name: string }) and defining the behavior a class must implement (interface Serializable { serialize(): string; deserialize(data: string): this }). For classes, the implements keyword explicitly declares that the class satisfies the interface, and TypeScript verifies it. Interface vs type alias: both define shapes in TypeScript. Interfaces can be extended with extends and merged (multiple interface declarations with the same name merge their properties — useful for augmenting third-party types). Type aliases can define union types, intersection types, and conditional types that interfaces can't. For simple object shapes, both work; prefer interfaces for public API contracts (they're more declaration-friendly) and type aliases for complex type expressions. In JavaScript without TypeScript, "interface" is informal — you define expected shapes through documentation and runtime checks. JSDoc type annotations provide some of the same benefits. Runtime interface checking (using Zod, Joi, or similar) validates that incoming data satisfies expected shapes.

Code Example

javascript
// TypeScript interfaces

// Data shape interface
interface User {
  id: number;
  email: string;
  name: string;
  role?: 'admin' | 'user';  // optional property
}

// Method signature interface
interface Repository {
  findById(id: number): Promise;
  findAll(): Promise;
  save(entity: T): Promise;
  delete(id: number): Promise;
}

// Class implementing an interface
class UserRepository implements Repository {
  async findById(id: number): Promise {
    // TypeScript verifies this implements all Repository methods
    return db.users.find(u => u.id === id) ?? null;
  }
  async findAll(): Promise { return db.users; }
  async save(user: User): Promise { /* ... */ return user; }
  async delete(id: number): Promise { /* ... */ }
}

// Structural typing: any object with the right shape satisfies
function greetUser(user: { name: string }) {
  return `Hello, ${user.name}`;
}

// Both work — structural compatibility
const admin = { name: 'Alice', role: 'admin', permissions: [] };
const guest = { name: 'Bob' };
greetUser(admin); // OK — has 'name'
greetUser(guest); // OK — has 'name'

Why It Matters for Engineers

Interfaces are TypeScript's killer feature. Without them, you're passing objects around without documentation of what shape they should be, leading to runtime "property undefined" errors. With interfaces, TypeScript catches type mismatches at compile time — before your code ships. This is the primary reason TypeScript adoption has grown dramatically. Understanding interfaces also enables better architecture: designing to interfaces rather than implementations (the "D" in SOLID — Dependency Inversion Principle) makes systems more flexible and testable. A function that accepts Repository works with any implementation — SQL database, MongoDB, in-memory mock — enabling testable code without a real database.

Related Terms

Type System · Class · Abstraction · Polymorphism

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Programming Foundations → →