Skip to content

Type vs. Interface

Rule

It’s called TypeScript not InterfaceScript

  • More expressive - Generally more versatile for complex type manipulations.
  • Type aliases - Acts as a true alias, capturing the exact shape of a type, while interfaces can be augmented later.
  • Union types - Only type allows you to create union types.
  • Primitive types - type can represent primitives directly, while interfaces can only describe object shapes
  • Tuple types - Only type can define tuples.
  • Mapped types - type allows for mapped types that transform existing types.

https://www.youtube.com/watch?v=OSEECveA_0w

Examples

🚨 DON’T

// Basic object definitions with interface
interface User {
    id: number;
    name: string;
    email: string;
    age: number;
}

// Using interface for union-like behavior (impossible)
interface Status {
    // Can't do: 'loading' | 'success' | 'error'
    value: string;
}

// Interface for primitive types (impossible)
interface UserId {
    // Can't alias a primitive directly
    value: number;
}

// Interface for tuple types (impossible)
interface Coordinates {
    // Can't define [number, number] directly
    x: number;
    y: number;
}

// Interface for conditional types (impossible)
interface ApiResponse<T> {
    // Can't do conditional logic like T extends string ? StringResponse : GenericResponse
    data: T;
    error?: string;
}

// Interface extending multiple interfaces with conflicts
interface A {
    prop: string;
}

interface B {
    prop: number;
}

interface Combined extends A, B {
} // Error: conflicting types

✅ DO

// Clean object type definitions
type User = {
    id: number;
    name: string;
    email: string;
    age: number;
};

// Union types for status/state management
type Status = "loading" | "success" | "error";

// Primitive type aliases
type UserId = number;

// Tuple types for coordinates, pairs, etc.
type Coordinates = [number, number];

// Mapped types for transformations
type ReadonlyUser = {
    readonly [K in keyof User]: User[K];
};

// Conditional types
type ApiResult<T> = T extends string ? { message: T } : { data: T };

// Intersection types
type Timestamped = {
    createdAt: Date;
    updatedAt: Date;
};
type UserWithTimestamp = User & Timestamped;

// Type guard helper type
type TypeGuard<T> = (value: unknown) => value is T;