Read-only properties
Not every field of a form is yours to fill in. The HR office prints the form's number in advance, at the print shop. The other fields are written in pen, while the printed number cannot be corrected: it is how the form is found in the archive.
In JavaScript every property of an object is like a field for a pen: it can be overwritten. Sometimes that is dangerous. If a user's id changes, every record that refers to that id will point at someone else.
The readonly modifier before a property makes it a read-only property: you can read it, but not write it once the object is created.
type User = {
readonly id: number;
name: string;
};
const user: User = { id: 1, name: "Ann" };
user.name = "Anna";
console.log(user.id, user.name); // 1 AnnaLet's go through the example line by line:
readonly id: numbermakes the propertyidread-only.- The literal
{ id: 1, name: "Ann" }setsidwhen the object is created, and that is allowed: the number is printed once, when the blank is issued. user.name = "Anna"changes an ordinary property, and the compiler does not object.- The program prints
1 Anna.
Mark as readonly whatever must not change once the object is created: ids, creation dates, initial settings.
Writing to a readonly property
Let's try to change the number after the object is created:
type User = {
readonly id: number;
name: string;
};
const user: User = { id: 1, name: "Ann" };
user.id = 2; // ✗error TS2540: Cannot assign to 'id' because it is a read-only property.The compiler answers that id cannot be assigned because it is a read-only property. The error sits on the line with the write, not in the type declaration: the compiler points at the place where the rule was broken.
readonly and const protect different things:
const userforbids putting another object into the variable, but does not stop the object's properties from changing;readonly idprotects one property, whatever variable holds the object, aconstor alet.
Only tsc checks it
readonly is part of the type, and types are erased. The compiler tsc reports the error in the example above, but it still writes the JavaScript file, and the word readonly is not in it. If that JavaScript runs, the assignment user.id = 2 works, and id becomes 2. The same happens when you run the file with node file.ts: it erases the types without checking them.
Here the analogy with the printed field stops working. Print-shop ink cannot be rubbed off, while readonly works only as long as the compiler checks the code.
Interviewers like to ask how readonly differs from Object.freeze. The answer: tsc checks readonly before the program runs, while the JavaScript function Object.freeze forbids writes to an object while the program runs.
One level of protection
A property may hold another object, like an address insert inside the form. Let's check whether readonly protects its fields:
type User = {
readonly id: number;
readonly address: { city: string };
};
const user: User = { id: 1, address: { city: "Oslo" } };
user.address.city = "Rome";
console.log(user.address.city); // RomeLet's see what is protected here and what is not:
readonly addressforbids putting another object intoaddress: the compiler rejectsuser.address = { city: "Rome" }with the same error,TS2540.- The property
cityinside the address is an ordinary one, souser.address.city = "Rome"passes. - The program prints
Rome.
readonly protects the property itself, but not the object it holds. To lock the city too, mark it as well: address: { readonly city: string }.
Common mistake
readonly was put before a variable so that it could not change:
readonly let total = 5; // ✗
console.log(total);error TS1024: 'readonly' modifier can only appear on a property declaration or index signature.The compiler answers that readonly goes only before a property in a type declaration. For a variable that must not be reassigned, JavaScript already has const: const total = 5;.
Key points
readonlybefore a property lets it be set when the object is created and forbids writes later: errorTS2540.- Only tsc checks
readonly: JavaScript has no such thing, and the write works when the program runs. readonlyprotects one level: the properties of a nested object stay changeable.constprotects a variable, andreadonlyprotects a property.