Immutable fields
1. The readonly keyword
To make a property immutable,
you write the readonly keyword before its name in an interface or a type.
In an interface
interface User {
readonly id: number;
name: string;
}
const user: User = { id: 1, name: "Tim" };
user.name = "Max"; // can be changed
user.id = 2; // Error: read-only property
readonlyguarantees thatidcannot be reassigned after initialization. Attempting to change the value causes a compilation error.
In a type (type)
type Product = {
readonly sku: string;
title: string;
};
const item: Product = { sku: "A-123", title: "Linen shirt" };
item.title = "Cotton shirt"; // allowed
item.sku = "B-456"; // ErrorWorks exactly the same way as in interfaces.
2. Immutability applies only at the top level
TypeScript makes readonly apply only to the property itself,
but does not protect nested objects:
interface Profile {
readonly user: {
name: string;
};
}
const p: Profile = { user: { name: "Tim" } };
p.user = { name: "Max" }; // Error (cannot reassign user)
p.user.name = "Max"; // allowed (nested fields can be changed)In other words, "deep" immutability is not created automatically.
3. How to make all fields immutable
TypeScript provides a built-in utility type Readonly<T>,
which turns every property of type T into readonly.
interface User {
id: number;
name: string;
}
type ImmutableUser = Readonly<User>;
const user: ImmutableUser = { id: 1, name: "Tim" };
user.id = 2; // Error
user.name = ""; // Error
Readonly<T>automatically addsreadonlybefore every field of the interface.
4. How to make "deep" immutability (nested fields too)
Out of the box, TypeScript only gives you shallow immutability.
If you need to prevent changes to nested objects,
you use recursive utilities, for example DeepReadonly.
Example implementation:
type DeepReadonly<T> = {
readonly [K in keyof T]: DeepReadonly<T[K]>;
};Example usage:
interface User {
id: number;
profile: {
name: string;
address: {
city: string;
};
};
}
type FrozenUser = DeepReadonly<User>;
const u: FrozenUser = {
id: 1,
profile: { name: "Tim", address: { city: "LA" } },
};
u.id = 2; // Error
u.profile.name = "Max"; // Error
u.profile.address.city = "NY"; // ErrorNow the object and all its nested fields are truly immutable.
5. readonly also works with arrays
You can make an array read-only using readonly or ReadonlyArray<T>:
const nums: readonly number[] = [1, 2, 3];
nums.push(4); // Error
const names: ReadonlyArray<string> = ["Tim", "Max"];
names[0] = "Anton"; // ErrorSuch an array cannot be modified (elements cannot be added, removed, or changed), but it can be read.
6. Summary table
| What you need | How to write it | Example |
|---|---|---|
| One read-only property | readonly id: number | interface User { readonly id: number } |
| All properties readonly | Readonly<Type> | type Immutable = Readonly<User> |
| Deep immutability | DeepReadonly<T> | recursive type |
| Read-only array | readonly T[] or ReadonlyArray<T> | readonly number[] |
Summary
In TypeScript, immutable properties are defined through
readonly.In an interface:
javascriptinterface User { readonly id: number }In a type:
javascripttype User = { readonly id: number }For all fields:
javascripttype Frozen = Readonly<User>This helps write reliable, predictable, and safe code, protecting data from accidental changes.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.