Коротка відповідь
juniorНезмінний об’єкт створюється за допомогою `Object.freeze`, `Object.seal` з додатковим перевіркою, або через конструктор класу із `readonly` полями. Також можна використати бібліотеку Immutable.js або Immer, що повертають нові копії при зміні. У TypeScript можна оголосити тип як `readonly` і комбінувати з `as const`.
Повне пояснення
Що це і навіщо
Незмінність (immutability) гарантує, що об’єкт після створення не змінюється – це зменшує ризик побічних ефектів, полегшує дебаггінг і підвищує передбачуваність коду, особливо в реактивних фреймворках.
Ключові принципи/терміни
Object.freeze(obj)– блокує додавання, видалення і зміну властивостей.Object.seal(obj)– забороняє додавання/видалення, але дозволяє змінювати існуючі.readonlyу TS – компілятор перевіряє, що властивість не змінюється.as const– типізує літерал як константу, роблячи йогоreadonly.- Immutable.js / Immer – бібліотеки, що забезпечують глибоку незмінність.
Як це працює
Object.freezeрекурсивно заморожує об’єкт і його вкладені структури.Object.sealлише забороняє зміну структури.- TS
readonly– компілятор генерує помилку при спробі присвоїти нове значення. - Immer приймає мутаційний код і повертає новий об’єкт, не змінюючи оригінал.
Практика і реалізація
// 1. freeze
const obj = { a: 1, b: { c: 2 } };
Object.freeze(obj);
obj.b.c = 3; // не зміниться
// 2. TS readonly
interface Config {
readonly host: string;
readonly port: number;
}
const cfg: Config = { host: 'localhost', port: 8080 };
// cfg.port = 3000; // TS error
import produce from 'immer';
const state = { count: 0 };
const next = produce(state, draft => {
draft.count += 1; // draft mutates, state stays
});
Тестування
import { expect } from 'vitest';
const obj = Object.freeze({ a: 1 });
test('freeze', () => {
expect(() => { obj.a = 2; }).toThrow();
});
Дизайн‑рішення
- Для простих об’єктів використовуйте
Object.freeze. - Якщо потрібна глибока незмінність, застосуйте Immer або Immutable.js.
- У TS комбінуйте
readonlyзas constдля статичної перевірки.
Оптимізація, типові помилки
Object.freezeне рекурсивний; для глибоких об’єктів треба рекурсію.Object.sealне блокує зміни значень, лише структуру.- Не забувайте про копіювання масивів/об’єктів перед
freeze.
Диагностика
- Якщо властивість змінюється, перевірте чи не використовується
Object.assignабо пряме присвоєння. - Для Immer перевірте, що ви не мутуєте draft поза функцією
produce.
Альтернативи
- Lodash _.cloneDeep +
Object.freeze. - Immutable.js Map/Seq для колекцій.
Часті помилки (8)
- Не рекурсивно заморожуєте вкладені об’єкти.
- Використовуєте
Object.sealзамістьfreezeі очікуєте незмінність. - Застосовуєте
readonlyлише в TS, але не перевіряєте runtime. - Не копіюєте масив перед
freeze, що змінює оригінал. - Застосовуєте Immer без
produce, що не створює новий об’єкт. - Не розумієте, що
Object.freezeне блокує додавання нових властивостей у дочірніх об’єктах. - Не перевіряєте, що
as constзастосовано до літералів. - Не тестуєте незмінність у юніт‑тестах, що призводить до непередбачуваних змін.