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
interfaceUser {
readonly id:number;
name:string;
}
In a type alias
typeProduct= {
readonly sku:string;
price:number;
};
In a class
classOrder {
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
letnumbers:readonlynumber[]= [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
interfaceAccount {
readonly id:number;
email:string;
}
constaccount:Account= {
id:101,
email:"alex@example.com"
};
account.id=202;// Error
The id remains fixed.
Example 2: Protected array parameter
functionprintScores(scores:readonlynumber[]) {
returnscores.join(", ");
}
The function cannot modify the incoming array.
Example 3: Class with immutable property
classUserProfile {
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:
constuser: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:
constfrozenUser=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:
interfaceCart {
readonly items:string[];
}
Why it breaks: The items reference cannot change, but the array itself can still be modified.
Correct approach:
interfaceCart {
readonly items:readonlystring[];
}
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