Lesson 6 / 25
Immutability in Practice
const, Object.freeze and readonly types.
What each tool really guarantees
const prevents reassigning the binding, not changing the object it points to: const a = []; a.push(1) is allowed. Object.freeze makes an object's own properties non-writable at runtime, but it is shallow (nested objects stay mutable) and in non-strict code writes fail silently rather than throwing. TypeScript's readonly modifiers, ReadonlyArray<T> / readonly T[], Readonly<T> and as const are compile-time checks only; they vanish in the emitted JavaScript. In practice, combine readonly types for safety during development with a team convention of never mutating inputs.
const, freeze and readonly compared
Comments describe what the compiler or runtime does.
const list = [1, 2, 3];
list.push(4); // allowed: const only fixes the binding
// list = []; // compile/runtime error: reassignment
const cfg = Object.freeze({ port: 8080, db: { host: 'localhost' } });
// cfg.port = 9090; // TypeScript error; at runtime ignored or TypeError in strict mode
cfg.db.host = 'prod'; // allowed: freeze is shallow
type Point = { readonly x: number; readonly y: number };
const p: Point = { x: 1, y: 2 };
// p.x = 5; // compile-time error only
function total(xs: readonly number[]): number {
// xs.push(1); // compile-time error: no push on readonly arrays
return xs.reduce((a, b) => a + b, 0);
}
const COLORS = ['red', 'green'] as const; // readonly ['red', 'green']Mark parameters readonly
Typing function parameters as readonly T[] or Readonly<T> documents and enforces that the function will not mutate what callers pass in.
Quick check: What does `const obj = {...}` guarantee in JavaScript?
- The object is copied on every read
- The object is deeply immutable
- The object is frozen at runtime
- The variable cannot be reassigned, but the object can still be mutated
Answer
The variable cannot be reassigned, but the object can still be mutated — const is about the binding, not the value.