How to Use readonly in TypeScript
What you’ll build or solve
You’ll declare properties that cannot be changed after they are set.
When this approach works best
Using readonly works best when you:
Learn TypeScript on Mimo
- Define identifiers such as IDs that must never change
- Share objects across modules and want to prevent mutation
- Protect arrays passed into functions from being modified
It also helps when modeling stable configuration or state.
This is a bad idea if the property is expected to change. Marking mutable data as readonly creates friction and unnecessary refactoring.
Prerequisites
- TypeScript installed
- A
.tsfile - Basic knowledge of interfaces and classes
Step-by-step instructions
Step 1: Add the readonly keyword to a property
Place readonly before a property name to prevent reassignment.
In an interface
interface User {
readonly id: number;
name: string;
}
In a type alias
type Product = {
readonly sku: string;
price: number;
};
In a class
class Order {
readonly orderId: number;
constructor(orderId: number) {
this.orderId = orderId;
}
}
You can assign a readonly property inside a constructor, but not after the object is created.
With arrays
let numbers: readonly number[] = [1, 2, 3];
numbers[0] = 10; // Error
numbers.push(4); // Error
The readonly keyword works the same way in all these contexts. It blocks reassignment at compile time.
What to look for
readonlyprevents reassignment, not reading- In classes, assignment is allowed in the constructor only
readonly Type[]blocks array mutations likepushreadonlydoes not freeze the object at runtime- For transforming an entire type to read-only, use the
Readonly<T>utility type from the utility types guide - For runtime immutability, use
Object.freeze()
Examples you can copy
Example 1: Stable identifier
interface Account {
readonly id: number;
email: string;
}
const account: Account = {
id: 101,
email: "alex@example.com"
};
account.id = 202; // Error
The id remains fixed.
Example 2: Protected array parameter
function printScores(scores: readonly number[]) {
return scores.join(", ");
}
The function cannot modify the incoming array.
Example 3: Class with immutable property
class UserProfile {
readonly userId: string;
displayName: string;
constructor(userId: string, displayName: string) {
this.userId = userId;
this.displayName = displayName;
}
}
userId stays constant after construction.
Common mistakes and how to fix them
Mistake 1: Expecting runtime immutability
You might assume:
const user: User = { id: 1, name: "Sam" };
Why it breaks: readonly is enforced by TypeScript at compile time. JavaScript can still mutate objects at runtime.
Correct approach:
const frozenUser = Object.freeze({ id: 1, name: "Sam" });
Use Object.freeze() if you need runtime protection.
Mistake 2: Making the property read-only but not the array contents
You might write:
interface Cart {
readonly items: string[];
}
Why it breaks: The items reference cannot change, but the array itself can still be modified.
Correct approach:
interface Cart {
readonly items: readonly string[];
}
This prevents both reassignment and array mutation.
Troubleshooting
- If you see “Cannot assign to property because it is a read-only property,” remove
readonlyif mutation is required. - If array elements still change, confirm you used
readonly Type[]. - If behavior differs at runtime, remember that
readonlydoes not freeze objects. - If you need full object immutability, combine compile-time checks with
Object.freeze().
Quick recap
- Add
readonlybefore a property to block reassignment - It works in interfaces, type aliases, classes, and arrays
- Assignment is allowed in class constructors
readonlyis compile-time only- Use
Readonly<T>for full-type transformation - Use
Object.freeze()for runtime immutability
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot