TypeScript

Interfaces

Define contracts for objects and classes using TypeScript interfaces.

Basic Interfaces

Interfaces define the shape of objects. They act as contracts that classes and object literals must fulfill.

// Basic interface
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // Optional property
  readonly createdAt: Date; // Cannot be changed after creation
}

// Interface with methods
interface Repository<T> {
  findById(id: number): Promise<T | null>;
  findAll(): Promise<T[]>;
  create(data: Omit<T, 'id'>): Promise<T>;
  update(id: number, data: Partial<T>): Promise<T>;
  delete(id: number): Promise<void>;
}

// Implementing an interface in a class
class UserRepository implements Repository<User> {
  private users: User[] = [];

  async findById(id: number): Promise<User | null> {
    return this.users.find(u => u.id === id) ?? null;
  }

  async findAll(): Promise<User[]> {
    return [...this.users];
  }

  async create(data: Omit<User, 'id'>): Promise<User> {
    const user: User = { ...data, id: Date.now() };
    this.users.push(user);
    return user;
  }

  async update(id: number, data: Partial<User>): Promise<User> {
    const index = this.users.findIndex(u => u.id === id);
    if (index === -1) throw new Error('User not found');
    this.users[index] = { ...this.users[index], ...data };
    return this.users[index];
  }

  async delete(id: number): Promise<void> {
    this.users = this.users.filter(u => u.id !== id);
  }
}

Extending Interfaces

Interfaces can extend other interfaces, enabling compositional type definitions.

interface Entity {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

interface User extends Entity {
  name: string;
  email: string;
}

interface AdminUser extends User {
  role: 'admin';
  permissions: string[];
  lastLogin: Date;
}

// Extending multiple interfaces
interface AuditableEntity extends Entity {
  createdBy: string;
  updatedBy: string;
}

interface AuditableUser extends User, AuditableEntity {
  // Has all properties from User and AuditableEntity
}

// Interface declaration merging (unique to interfaces)
interface Config {
  theme: 'dark' | 'light';
}

interface Config {
  language: string; // Merged with the above Config
}

// Now Config has both theme and language
const config: Config = {
  theme: 'dark',
  language: 'en',
};

Interface vs Type Alias

Both interface and type can define object shapes. Here are the key differences to help you choose.

// Both can define object shapes
interface UserInterface {
  name: string;
  age: number;
}

type UserType = {
  name: string;
  age: number;
};

// Difference 1: Declaration merging (only interfaces)
interface Window {
  myCustomProperty: string; // Extends built-in Window type
}

// Difference 2: Computed properties (only types)
type Keys = 'name' | 'email';
type UserFromKeys = {
  [K in Keys]: string; // Can't do this with interface
};

// Difference 3: Union/intersection (types are more powerful)
type Result = 'success' | 'error'; // Union - can't do with interface
type Combined = UserInterface & { role: string }; // Intersection

// Recommendation:
// Use 'interface' for object shapes and class contracts
// Use 'type' for unions, intersections, mapped types, and aliases

The TypeScript team recommends using interface for public API definitions and type for complex type transformations.