readonly Properties & Immutability
Lock individual fields against reassignment, and understand what readonly does not do.
Prefix a property with readonly and the compiler forbids reassigning it after construction. It is perfect for identifiers, configuration, and values that should be set once.
interface Account {
readonly id: string;
balance: number;
}
const acc: Account = { id: "a1", balance: 0 };
acc.balance = 100; // fine
acc.id = "a2"; // Error: id is readonlyIt is shallow
readonly stops reassignment of the property itself, not mutation of what it points to. A readonly array reference can still be swapped if... actually no β but a readonly field holding an object lets you still mutate that object's inner fields. For deep immutability you nest readonly or use Readonly<T> / ReadonlyArray<T>.
Compile-time only
Like all types, readonly vanishes after compilation. It documents and enforces intent during development; at runtime the object is an ordinary, mutable JavaScript object.
Example
When to use it
- An application config object is typed Readonly<Config> so no module can accidentally overwrite a setting after startup.
- A pure function accepts Readonly<Order> to signal it only reads, never mutates, the order it receives.
- A React component uses readonly props to make the contract explicit that the component must not modify its inputs.
More examples
Readonly utility type
Readonly<Point> prevents mutation of x and y while spread creates a new mutable copy with the desired change.
interface Point { x: number; y: number; }
const origin: Readonly<Point> = { x: 0, y: 0 };
origin.x = 1; // Error: Cannot assign to 'x' because it is a read-only property
// Create a moved copy instead
const moved: Point = { ...origin, x: 5 };Readonly in function parameter
Annotating the parameter as Readonly communicates that the function will not mutate the array or its items.
function totalPrice(items: Readonly<{ price: number }[]>): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
const cart = [{ price: 10 }, { price: 25 }];
console.log(totalPrice(cart));Deep readonly with as const
as const applies deep readonly to nested objects, while Readonly<T> only applies shallowly at the top level.
const settings = {
db: { host: 'localhost', port: 5432 },
cache: { ttl: 300 },
} as const;
// settings.db.port = 3306; // Error: read-only
type Settings = typeof settings; // deeply readonly
Discussion