История вопроса:
Spread (...) и Rest (...) — одни из самых популярных современных синтаксических конструкций, пришедших из ES6. В TypeScript они приобрели типовую строгость, поэтому использование Spread и Rest стало не только удобным, но и безопасным, если правильно соблюдать типовые ограничения.
Проблема:
Spread и Rest-операции становятся источником ошибок, если не учтён тип значений или смешиваются несовместимые типы (например, объединяются объекты с несовместимыми свойствами или массивы разных типов).
Решение:
В TypeScript Spread используется для копирования или объединения свойств объектов и элементов массивов. Rest позволяет собирать "остаточные" элементы в функцию или массив, при этом строгая типизация проверяет несовместимые случаи, предотвращая ошибки на этапе компиляции.
Пример кода:
// Spread с объектами const base = { a: 1, b: 2 }; const extended = { ...base, c: 3 }; // extended: { a: number; b: number; c: number } // Rest в параметрах функции function sum(...args: number[]): number { return args.reduce((acc, val) => acc + val, 0); } // Spread в массивах const arr = [1, 2, 3]; const newArr = [...arr, 4, 5];
Ключевые особенности:
Чем отличается копирование объекта через Spread от присваивания по ссылке?
Spread создаёт новый объект с копией всех перечисляемых свойств, но если в объекте есть вложенные объекты, они копируются по ссылке (shallow copy).
const base = { a: 1, nested: { x: 2 } }; const copy = { ...base }; copy.nested.x = 42; console.log(base.nested.x); // 42
Можно ли с помощью Spread копировать только определённые свойства?
Нет, Spread копирует все перечисляемые свойства объекта. Для выборки отдельных используют деструктуризацию:
const { a, ...rest } = { a: 1, b: 2, c: 3 }; // rest: { b: 2, c: 3 }
Что произойдет, если объединить через Spread два объекта с одинаковыми свойствами? Как это влияет на типизацию?
Свойства из последнего объекта перекроют свойства предыдущих. При этом тип последнего свойства будет считаться финальным:
const a = { val: 1 }; const b = { val: 'hello' }; const merged = { ...a, ...b }; // merged: { val: string } (а не number)
В проекте решили объединить параметры конфигурации через Spread. Один из объектов имел свойство timeout: number, другой — timeout: string. При этом никаких ошибок не было замечено до рантайма, пока функция не сломалась из-за неправильного типа.
Плюсы:
Минусы:
Использовали типизированный Spread для объединения строгих интерфейсов, а также деструктуризацию для отделения ненужных полей. Ошибки компилятор выявил сразу.
Плюсы:
Минусы: