Свойства только для чтения
Не все поля анкеты заполняете вы. Номер анкеты отдел кадров печатает заранее, ещё в типографии. Остальные поля вписывают ручкой, а напечатанный номер исправить нельзя: по нему анкету находят в архиве.
В JavaScript любое свойство объекта похоже на поле для ручки: его можно перезаписать. Иногда это опасно. Если у пользователя поменяется id, все записи, которые ссылаются на этот id, укажут на кого-то другого.
Модификатор readonly перед свойством делает его свойством только для чтения: читать его можно, а записывать после создания объекта — нет.
type User = {
readonly id: number;
name: string;
};
const user: User = { id: 1, name: "Ann" };
user.name = "Anna";
console.log(user.id, user.name); // 1 AnnaРазберём пример по строкам:
readonly id: number— свойствоidтолько для чтения.- Литерал
{ id: 1, name: "Ann" }задаётidпри создании объекта, и это разрешено: номер печатают один раз, когда выдают бланк. user.name = "Anna"меняет обычное свойство, и компилятор не возражает.- Программа печатает
1 Anna.
Помечайте readonly то, что не должно меняться после создания объекта: номера, даты создания, исходные настройки.
Запись в readonly-свойство
Давайте попробуем поменять номер после создания объекта:
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.Компилятор отвечает: «нельзя присвоить id, потому что это свойство только для чтения». Ошибка стоит на строке записи, а не в объявлении типа: компилятор показывает место, где правило нарушили.
readonly и const защищают разное:
const userзапрещает положить в переменную другой объект, но менять свойства объекта не мешает;readonly idзащищает одно свойство, в какой бы переменной ни лежал объект — хоть вconst, хоть вlet.
Проверяет только tsc
readonly — часть типа, а типы стираются. Компилятор tsc сообщит об ошибке в примере выше, но JavaScript-файл всё равно запишет, и слова readonly в нём не будет. Если этот JavaScript запустить, присваивание user.id = 2 сработает, и id станет 2. То же случится при запуске через node file.ts: он стирает типы, не проверяя их.
Здесь аналогия с напечатанным полем перестаёт работать. Типографскую краску не сотрёшь, а readonly действует, только пока код проверяет компилятор.
На собеседованиях любят спрашивать, чем readonly отличается от Object.freeze. Ответ: readonly проверяет tsc до запуска, а функция Object.freeze из JavaScript запрещает запись в объект во время работы программы.
Защита в один уровень
В свойстве может лежать другой объект — как вкладыш с адресом внутри анкеты. Проверим, защищает ли readonly его поля:
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); // RomeДавайте посмотрим, что здесь защищено, а что нет:
readonly addressзапрещает положить вaddressдругой объект:user.address = { city: "Rome" }компилятор не пропустит, ошибка та же —TS2540.- Свойство
cityвнутри адреса — обычное, поэтомуuser.address.city = "Rome"проходит. - Программа печатает
Rome.
readonly защищает само свойство, но не объект, который в нём лежит. Чтобы закрыть и город, пометьте его тоже: address: { readonly city: string }.
Частая ошибка
readonly поставили перед переменной, чтобы её нельзя было менять:
readonly let total = 5; // ✗
console.log(total);error TS1024: 'readonly' modifier can only appear on a property declaration or index signature.Компилятор отвечает, что readonly ставят только перед свойством в объявлении типа. Для переменной, которую нельзя переприсвоить, в JavaScript уже есть const: const total = 5;.
Главное
readonlyперед свойством разрешает задать его при создании объекта и запрещает запись потом: ошибкаTS2540.- Проверяет
readonlyтолько tsc: в JavaScript его нет, и при запуске запись сработает. readonlyзащищает один уровень: свойства вложенного объекта остаются изменяемыми.constзащищает переменную, аreadonly— свойство.