TypeScript Interfaces and Types: When to Use Which

One of the most common questions I get about TypeScript: should I use an interface or a type? Both can describe object shapes, but they have subtle differences. Here’s when to use each.

The Quick Answer

Use interfaces for object shapes and public APIs. Use types for unions, intersections, and complex type manipulations. When in doubt, use interfaces.

Interfaces

interface User {
  id: number;
  name: string;
  email?: string;  // Optional
  readonly createdAt: Date;  // Read-only
}

// Extending interfaces
interface Employee extends User {
  department: string;
  salary: number;
}

// Declaration merging (interfaces can be extended later)
interface User {
  phone?: string;  // Adds to original interface
}

Type Aliases

// Object type
type User = {
  id: number;
  name: string;
};

// Union types - can't do this with interfaces
type Status = 'pending' | 'approved' | 'rejected';

// Intersection types
type Employee = User & {
  department: string;
};

// Utility types
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type UserKeys = keyof User;  // 'id' | 'name'

Key Differences

  • Declaration merging: Interfaces can be extended; types cannot
  • Unions/Intersections: Only types can define unions
  • Primitives: Only types can alias primitives
  • Error messages: Interfaces often give better error messages

My Guidelines

// Use interface for objects
interface ApiResponse {
  data: unknown;
  status: number;
}

// Use type for unions
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

// Use type for function signatures
type Handler = (req: Request) => Response;

// Use type for computed/mapped types
type Nullable<T> = T | null;

References


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.