Record и Tuple (proposal)
Record (
#{ }) и Tuple (#) — предложение TC39 (Stage 2) для добавления в JavaScript встроенных примитивных, глубоко иммутабельных структур данных, сравниваемых по значению, а не по ссылке.
Зачем нужно
Одна из главных проблем JavaScript — сравнение объектов и массивов по ссылке: {a:1} !== {a:1}. Это создаёт проблемы в React (лишние ре-рендеры), при сравнении состояния и в иммутабельных структурах данных. Record и Tuple решают это на уровне языка, предлагая примитивную семантику для составных данных.
Где используется
- Ключи Map (объекты нельзя использовать как уникальные ключи без хака)
- Сравнение состояния в React/Vue без глубокого equals
- Конфигурационные объекты, которые должны быть неизменяемы
- Иммутабельное программирование без библиотек (Immer, Immutable.js)
Основной контент
Синтаксис (proposal, не в продакшне)
// Record — иммутабельный объект
const point = #{ x: 1, y: 2 };
const config = #{ host: 'localhost', port: 8080 };
// Tuple — иммутабельный массив
const coords = #[1, 2, 3];
const rgb = #[255, 0, 0];
// Сравнение по значению (!)
console.log(#{ x: 1 } === #{ x: 1 }); // true (в отличие от обычных объектов!)
console.log(#[1, 2] === #[1, 2]); // true
// Обычные объекты
console.log({ x: 1 } === { x: 1 }); // false
Иммутабельность
const record = #{ a: 1, b: 2 };
// Нельзя мутировать — TypeError
// record.a = 10;
// record.c = 3;
// Создание нового Record с изменениями (spread работает)
const updated = #{ ...record, a: 10 };
console.log(updated === #{ a: 10, b: 2 }); // true
console.log(record === #{ a: 1, b: 2 }); // true (оригинал не изменился)
Ограничения proposal
// Только примитивы и другие Record/Tuple внутри
const valid = #{ x: 1, arr: #[1, 2, 3] };
// const invalid = #{ fn: => {} }; // TypeError — функции не допускаются
// const invalid2 = #{ obj: { a: 1 } }; // TypeError — обычные объекты не допускаются
// Методы через глобальный Record и Tuple
const r = #{ a: 1, b: 2 };
Record.keys(r); // ['a', 'b']
Record.values(r); // [1, 2]
const t = #[1, 2, 3];
Tuple.from([4, 5, 6]); // #[4, 5, 6]
t.pushed(4); // #[1, 2, 3, 4] (возвращает новый Tuple)
Как сейчас (до стандартизации)
// Аналог через Object.freeze
const record = Object.freeze({ x: 1, y: 2 });
// Но сравнение по ссылке — этот паттерн не решает проблему ===
// Сравнение через JSON (ограничено, но работает для простых случаев)
const equals = (a, b) => JSON.stringify(a) === JSON.stringify(b);
console.log(equals({ x: 1 }, { x: 1 })); // true
// Библиотека Immer для иммутабельных обновлений
import { produce } from 'immer';
const state = { count: 0 };
const newState = produce(state, draft => { draft.count++; });
Частые ошибки
- Путаница со Stage — на 2026 год Record и Tuple находятся на Stage 2 TC39, не реализованы в браузерах. Не используйте в продакшне без полифила.
- Ожидание полной замены объектов — Record/Tuple не заменяют объекты. Они примитивы, не имеют методов, не поддерживают функции как значения.
Object.freezeне то же самое —freezeне меняет семантику сравнения. Только поверхностная неизменяемость.