Как сделать объект иммутабельным в TypeScript

Ответ

В TypeScript можно сделать объект иммутабельным несколькими способами:

  1. Использование readonly:
type ImmutableUser = {
  readonly name: string;
  readonly age: number;
};
  1. С помощью Readonly<T> утилиты:
type User = { name: string; age: number };
const user: Readonly<User> = { name: 'Alice', age: 30 };
// user.name = 'Bob' - ошибка компиляции
  1. Глубокое замораживание через Object.freeze():
const deepFreeze = (obj: any) => {
  Object.freeze(obj);
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') deepFreeze(obj[key]);
  });
};

const user = { name: 'Alice', address: { city: 'NY' } };
deepFreeze(user);

Для сложных структур лучше использовать библиотеки типа Immutable.js. В React часто используют Immer для работы с иммутабельными состояниями.

Ответ 18+ 🔞

Слушай, а вот этот TypeScript, он же как ревнивый муж, блядь — хочет всё контролировать, чтобы ты нигде не накосячил. И иммутабельность там — это вообще святое, как будто тебе говорят: "Не трогай, сука, это уже готовое, не порть!"

Ну, например, взял ты объект, описал его и навешал readonly. Это как табличку "руками не трогать" повесить, ёпта.

type ImmutableUser = {
  readonly name: string;
  readonly age: number;
};

Попробуй теперь изменить name — получишь по ебалу от компилятора, сразу. "Куда лезешь, мудак? Я сказал — readonly!"

А если лень каждый раз readonly писать, есть же готовая утилита Readonly<T>. Подсунул ей свой тип — и всё, пиздец, объект замурован.

type User = { name: string; age: number };
const user: Readonly<User> = { name: 'Alice', age: 30 };
// user.name = 'Bob' - ошибка компиляции

Попробуй теперь user.name поменять — TypeScript тебе такую ошибку влепит, что мало не покажется. "Ты че, охренел? Я же сказал — Readonly!"

Но это всё, блядь, поверхностно. Если внутри объекта ещё другие объекты сидят — это ж просто пиздец, они же остаются изменяемыми! Ну, как дети, которых в угол поставили, а они всё равно друг другу рожицы строят.

Тут надо Object.freeze() применять, да ещё и рекурсивно, чтоб наверняка.

const deepFreeze = (obj: any) => {
  Object.freeze(obj); // Заморозил верхний уровень
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') deepFreeze(obj[key]); // И пошёл внутрь, как в подвал, всё замораживать
  });
};

const user = { name: 'Alice', address: { city: 'NY' } };
deepFreeze(user);

Вот теперь всё, пиши пропало. Объект — как муха в янтаре, блядь. Ни тронуть, ни подышать.

А для настоящих, сложных, ебанистических структур — лучше сразу брать библиотеки. Immutable.js — это вообще отдельная религия, там свои типы данных, всё своё. А в React, например, Immer часто юзают — он как хитрая жопа: ты вроде бы изменяешь состояние как обычно, а он за кулисами тебе новый объект создаёт, иммутабельный. Удобно, ёпта!

Короче, вариантов — овердохуища. Выбирай, не ошибёшься. Главное — не лезь менять то, что менять нельзя, а то компилятор тебе такую мозгоёбку устроит, что будешь неделю отходить.