Readonly свойства

readonly в TypeScript — модификатор свойства или элемента кортежа, запрещающий переприсвоение после инициализации на уровне системы типов.

Зачем нужно

readonly защищает от случайных мутаций объектов и массивов на этапе компиляции. Это особенно важно в функциональном стиле, при работе с иммутабельным state (Redux), и для защиты конфигурационных объектов. В отличие от const, readonly работает на уровне свойств, а не переменных.

Где используется

  • Иммутабельные value objects и DTO
  • Конфигурационные объекты
  • readonly массивы в функциях (принимать без мутации)
  • Parameter properties в конструкторах
  • Защита полей класса от изменения снаружи

Основной контент

readonly в интерфейсах и type alias

interface Point {
  readonly x: number;
  readonly y: number;
}

const p: Point = { x: 0, y: 0 };
// p.x = 1; // Error: Cannot assign to 'x' because it is a read-only property
// p.y = 2; // Error

// Но объект можно переприсвоить (readonly не влияет на переменную)
let point: Point = { x: 0, y: 0 };
point = { x: 1, y: 2 }; // OK — переприсвоение переменной

readonly в классах

class Circle {
  readonly radius: number;
  readonly center: Point;

  constructor(radius: number, center: Point) {
    this.radius = radius; // OK — в конструкторе
    this.center = center;
  }

  scale(factor: number): Circle {
    // this.radius *= factor; // Error — нельзя изменить readonly
    return new Circle(this.radius * factor, this.center);
  }
}

readonly массивы

// ReadonlyArray<T> или readonly T
function sum(numbers: readonly number): number {
  // numbers.push(4); // Error — push не доступен
  // numbers[0] = 10; // Error
  return numbers.reduce((acc, n) => acc + n, 0);
}

const nums = [1, 2, 3];
sum(nums); // OK — number присваивается readonly number

const frozen: ReadonlyArray<string> = ["a", "b", "c"];
// frozen.push("d"); // Error

readonly кортежи

type Pair = readonly [number, string];

const p: Pair = [1, "hello"];
// p[0] = 2; // Error
// p.push(3); // Error

Разница const и readonly

const obj = { name: "Alice" };
obj.name = "Bob"; // OK — const запрещает переприсвоение переменной, не мутацию

const readonlyObj: { readonly name: string } = { name: "Alice" };
// readonlyObj.name = "Bob"; // Error — readonly запрещает мутацию свойства

Readonly утилитарный тип

interface Config {
  host: string;
  port: number;
}

// Все поля readonly
const config: Readonly<Config> = { host: "localhost", port: 3000 };
// config.host = "example.com"; // Error

Важно: readonly — только на уровне типов

// В рантайме readonly не защищает от мутации!
const obj = { x: 1 } as { readonly x: number };
(obj as { x: number }).x = 2; // Компилятор запретит без cast, но в JS это работает

// Для рантайм-защиты используйте Object.freeze:
const frozen = Object.freeze({ x: 1, y: 2 });
// frozen.x = 5; // TypeError в рантайме

Частые ошибки

  • Думать что readonly = constconst запрещает переприсвоение переменной; readonly запрещает мутацию свойства.
  • Ожидать защиты вложенных объектовreadonly поверхностный; вложенные объекты можно мутировать.
  • Не использовать readonly для параметров-массивов — без readonly number функция может случайно мутировать входной массив.
  • Забывать что в рантайме readonly не работает — для реальной защиты нужен Object.freeze.

Связанные темы

Ресурсы