TypeScript SeriesPart 2 of 4

TypeScript Types and Interfaces — What's the Difference?

Nov 24, 2024·2 min read·
TypeScriptTypesInterfaces

One of the first questions TypeScript developers hit is: should I use type or interface? They look almost identical on the surface, and for simple cases they're interchangeable. But each has a niche where it genuinely shines.

Defining Object Shapes — Both Work

// Using interface
interface User {
  id: number;
  name: string;
  email: string;
}

// Using type alias
type User = {
  id: number;
  name: string;
  email: string;
};

For simple object shapes, the choice is mostly stylistic. Here's how to think about when to pick one.

Interfaces Are for Contracts

Interfaces are ideal when you're describing a contract — something a class or object must conform to. They support declaration merging, which is useful when working with third-party libraries.

interface Repository<T> {
  findById(id: string): Promise<T | null>;
  save(entity: T): Promise<T>;
  delete(id: string): Promise<void>;
}

class UserRepository implements Repository<User> {
  async findById(id: string) { /* ... */ }
  async save(user: User) { /* ... */ }
  async delete(id: string) { /* ... */ }
}

Type Aliases Are for Unions and Computed Types

type becomes necessary when you need union types, intersection types, or any computed/conditional type:

// Union type — only possible with type
type Status = "idle" | "loading" | "success" | "error";

// Intersection type
type AdminUser = User & { permissions: string[] };

// Conditional type
type NonNullable<T> = T extends null | undefined ? never : T;

// Mapped type
type ReadOnly<T> = {
  readonly [K in keyof T]: T[K];
};

You can't express these with interface.

The Practical Rule

  • Use interface for object shapes that represent real-world entities (User, Product, ApiResponse)
  • Use type for unions, intersections, utility types, and function signatures
  • Be consistent within a codebase — pick one default and only switch when the other is strictly necessary
// Recommended pattern
interface BlogPost {
  id: string;
  title: string;
  status: PostStatus; // using the type alias from below
}

type PostStatus = "draft" | "published" | "archived";
type PostWithAuthor = BlogPost & { author: User };

In Part 3, we'll look at generics — the feature that makes TypeScript's type system truly powerful for building reusable utilities.