Skip to content

Immutability

Rule

TypeScript's type system allows you to mark individual properties on an interface/class as readonly. This allows you to work in a functional way (an unexpected mutation is bad).

For more advanced scenarios there is a built-in type Readonly that takes a type and marks all of its properties as readonly using mapped types (see mapped types).

  • Readability – readonly makes it clear which properties should never change.
  • Safety – prevents accidental mutations that could introduce bugs.
  • Compiler help – TypeScript enforces immutability at compile time.
  • Functional style – supports immutability-first programming patterns.
  • Reasoning – reduces side effects, making code easier to understand.
  • Flexibility – Readonly can lock down entire objects when needed.

Examples

🚨 DON’T

type User = {
    id: string;
    name: string;
};

const user: User = {id: "123", name: "Alice"};
user.id = "456"; // ❌ allowed, but unsafe

✅ DO

type User = {
    readonly id: string;
    name: string;
};

const user: User = {id: "123", name: "Alice"};
user.name = "Bob"; // ✅ allowed
user.id = "456";   // ❌ error: id is readonly

type Todo = {
    title: string;
    completed: boolean;
};

const todo: Readonly<Todo> = {title: "Learn TS", completed: false};
todo.completed = true; // ❌ error: all props are readonly