object-transforms

Readonly<T>

Marks every property of T as readonly, preventing reassignment after construction. Only enforced at compile-time — the underlying object is still mutable at runtime.

type Readonly<T> = { readonly [P in keyof T]: T[P] }

Usage patterns

Pattern Purpose
Readonly<T> Freeze all top-level properties at the type level
ReadonlyArray<T> Companion built-in for immutable arrays
as const Assertion that produces deep-readonly literal types
DeepReadonly<T> Custom recursive variant — Readonly itself is shallow

Examples

interface Point { x: number; y: number }
const p: Readonly<Point> = { x: 1, y: 2 };
p.x = 5; // Error

Attempted mutation is caught at compile time

function freeze<T>(o: T): Readonly<T> {
  return Object.freeze(o);
}

Type-level counterpart of Object.freeze — but only shallow like Readonly

type Config = Readonly<{ endpoints: { api: string } }>;
const c: Config = { endpoints: { api: '' } };
c.endpoints.api = 'x'; // Allowed!

Nested props remain mutable — Readonly is shallow

const tuple = [1, 2, 3] as const; // readonly [1, 2, 3]

`as const` gives deep-readonly literal inference, unlike Readonly<T>

Gotcha

Readonly is shallow — nested objects/arrays stay writable. It also does not survive JSON.parse or spread copies; use `as const` or a DeepReadonly helper for true immutability.

Related utility types

← All TS utility types · TypeScript vs JavaScript · any vs unknown vs never