Как работает readonly в TypeScript?
MiddleTypeScript #typescript
Вопрос
Как работает модификатор readonly в TypeScript?
Короткий ответ
readonly запрещает переприсваивание свойства объекта или элемента массива после инициализации — но это проверка только на этапе компиляции, в скомпилированном JavaScript никакой защиты не остаётся.
Подробный ответ
readonly можно применить к:
- Свойству интерфейса/типа — значение можно установить один раз (в объектном литерале или конструкторе класса), дальше присваивание запрещено компилятором.
- Массиву —
readonly T[]илиReadonlyArray<T>запрещает мутирующие методы (push,pop,splice, присваивание по индексу). - Полю класса — можно установить только при объявлении или внутри конструктора.
Важный нюанс: readonly — это поверхностная (shallow) защита. Если свойство — объект, readonly запрещает переприсвоить сам объект, но не запрещает изменять его внутренние поля.
Также стоит помнить: это чисто компиляторная проверка типов (стирается при транспиляции в JS), а не рантайм-защита вроде Object.freeze() — в обычном JavaScript-коде или после приведения типа readonly можно обойти.
Пример
interface User {
readonly id: number
name: string
address: { city: string }
}
const user: User = { id: 1, name: 'Alice', address: { city: 'Tashkent' } }
user.name = 'Bob' // OK
user.id = 2 // Error: Cannot assign to 'id' because it is a read-only property
user.address.city = 'Samarkand' // OK — readonly не глубокий (shallow)
const nums: readonly number[] = [1, 2, 3]
nums.push(4) // Error: Property 'push' does not exist on type 'readonly number[]'
class Point {
readonly x: number
constructor(x: number) {
this.x = x // OK — присваивание в конструкторе разрешено
}
}
Дополнительные вопросы
- Чем
readonlyв TypeScript отличается отObject.freeze()в рантайме? - Как сделать вложенные свойства объекта тоже неизменяемыми (глубокий readonly)?
- Что делает утилитарный тип
Readonly<T>?