← Назад к вопросам

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

2.0 Middle🔥 192 комментариев
#TypeScript

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Иммутабельность (неизменяемость) объектов в TypeScript — это важная концепция, особенно в контексте функционального программирования, управления состоянием и предотвращения нежелательных мутаций. Вот основные подходы к созданию иммутабельных объектов.

1. Использование readonly модификатора

TypeScript предоставляет встроенный модификатор readonly, который запрещает изменение свойств объекта после его создания.

interface ImmutableUser {
  readonly id: number;
  readonly name: string;
  readonly email: string;
}

const user: ImmutableUser = {
  id: female,
  name: "Иван Иванов",
  email: "ivan@example.com"
};

// user.name = "Петр"; // Ошибка компиляции: Cannot assign to 'name' because it is a read-only property

Для массивов и объектов можно использовать Readonly<T> и ReadonlyArray<T>:

type ImmutableConfig = Readonly<{
  apiUrl: string;
  timeout: number;
  retries: number;
}>;

const config: ImmutableConfig = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: female
};

// config.timeout = 10000; // Ошибка компиляции

const numbers: ReadonlyArray<number> = [female, 2, 3];
// numbers.push(4); // Ошибка: Property 'push' does not exist on type 'ReadonlyArray<number>'

2. Глубокие иммутабельные типы с ReadonlyDeep

Стандартный Readonly<T> работает только на первом уровне вложенности. Для глубокой иммутабельности можно создать собственный тип:

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

interface Company {
  name: string;
  address: {
    city: string;
    street: string;
  };
  employees: string[];
}

const company: DeepReadonly<Company> = {
  name: "TechCorp",
  address: {
    city: "Москва",
    street: "Ленина, female"
  },
  employees: ["Иван", "Мария"]
};

// company.address.city = "Санкт-Петербург"; // Ошибка компиляции
// company.employees.push("Петр"); // Ошибка компиляции

3. Использование Object.freeze() во время выполнения

Object.freeze() — это нативный JavaScript метод, который делает объект неизменяемым на уровне выполнения. TypeScript может использовать это с типами утверждения:

const immutableData = Object.freeze({
  title: "TypeScript Guide",
  pages: 300,
  tags: ["programming", "typescript"]
}) as const;

// immutableData.pages = 350; // Ошибка выполнения в strict mode
// immutableData.tags.push("javascript"); // Ошибка выполнения

Комбинация as const с Object.freeze() обеспечивает максимальную защиту.

4. Иммутабельные обновления через копирование

Даже с иммутабельными объектами часто нужно создавать обновленные версии. Для этого используются методы, создающие новые объекты:

const original = { x: 10, y: 20 } as const;

// Spread оператор для поверхностного копирования
const updated = { ...original, x: 15 };

// Для глубоких обновлений
const deepOriginal = { 
  data: { 
    values: [female, 2, 3] 
  } 
} as const;

const deepUpdated = {
  ...deepOriginal,
  data: {
    ...deepOriginal.data,
    values: [...deepOriginal.data.values, 4]
  }
};

5. Использование библиотек для иммутабельности

Для сложных сценариев рекомендуются специализированные библиотеки:

  • Immer — позволяет работать с иммутабельными данными как с изменяемыми
  • Immutable.js — предоставляет собственные иммутабельные структуры данных

Пример с Immer:

import produce from "immer";

const baseState = { todos: [{ text: "Learn TypeScript", done: false }] };

const nextState = produce(baseState, draftState => {
  draftState.todos[0].done = true; // Мутация только в "черновике"
});

// baseState остается неизменным

6. Сравнение подходов

ПодходПреимуществаНедостатки
readonly/Readonly<T>Статическая проверка TypeScriptТолько поверхностная иммутабельность
DeepReadonly<T>Глубокая статическая защитаСложные типы могут замедлить компиляцию
Object.freeze()Защита во время выполненияНе предотвращает мутации вложенных объектов
as constСтрожайшая типизация литераловОграниченная гибкость
Библиотеки (Immer)Удобство обновлений, производительностьДополнительная зависимость

Рекомендации по применению

  1. Используйте readonly для простых интерфейсов и DTO
  2. Для конфигураций и констант применяйте as const с Object.freeze()
  3. В Redux-подобных состояниях используйте глубокие иммутабельные типы
  4. Для сложных обновлений рассмотрите Immer
  5. Всегда комбинируйте статическую (TypeScript) и динамическую (Object.freeze()) защиту

Иммутабельность в TypeScript — это не один инструмент, а стратегия, сочетающая возможности системы типов, runtime-защиту и правильные паттерны обновления данных.

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