const и изменяемые объекты
Запечатанная банка const хранит одно значение, и TypeScript подписывает её точно. С числом всё ясно: const limit = 5 останется пятёркой навсегда. А что, если в константе лежит объект?
const cart = { total: 0 };
const scores = [3, 5];
cart.total = 1500;
scores.push(4);
console.log(cart.total, scores.length); // 1500 3Программа компилируется без ошибок и печатает 1500 3. Давайте разберём её по строкам:
const cart = { total: 0 };— константаcartхранит объект со свойствомtotal.const scores = [3, 5];— константаscoresхранит массив из двух чисел.cart.total = 1500;меняет свойство внутри объекта, иtscне возражает.scores.push(4);добавляет элемент в тот же массив — тоже без ошибки.console.logпечатает1500 3: вtotalтеперь1500, а в массиве три элемента.
Объект и массив изменились, хотя оба объявлены через const. Что же тогда запрещает const?
Что запрещает const
const запрещает дать имени новое значение — записать в cart другой объект, даже точно такой же формы:
const cart = { total: 0 };
cart = { total: 1500 }; // ✗error TS2588: Cannot assign to 'cart' because it is a constant.Сообщение значит: «нельзя присвоить cart новое значение, потому что это константа». В JavaScript эта строка упала бы только при запуске, с ошибкой TypeError: Assignment to constant variable. TypeScript находит её раньше — при компиляции.
Что можно менять внутри
Константа всё время держит одно и то же значение — один и тот же объект или массив. А то, что лежит внутри него, const не охраняет:
- свойства объекта:
cart.total = 1500; - элементы массива:
scores[0] = 10; - методы, которые меняют массив:
scores.push(4).
Здесь аналогия с запечатанной банкой заканчивается: она верна для чисел и строк, у которых нет частей. Объект больше похож на комод с ящиками. const намертво приклеивает наклейку cart к одному комоду, но ящики в нём по-прежнему выдвигаются.
Этот вопрос часто задают на собеседовании: «Можно ли изменить объект, объявленный через const?» Ответ: его свойства — можно, а записать в имя другой объект — нельзя.
Типы свойств остаются общими
Вы уже видели, что const point = { x: 5 } получает тип { x: number; }. С cart то же самое: его тип — { total: number; }. Поэтому в свойство можно записать любое другое число, но не значение другого типа:
const cart = { total: 0 };
cart.total = "бесплатно"; // ✗error TS2322: Type 'string' is not assignable to type 'number'.const не охраняет содержимое объекта, а тип охраняет: менять total можно, но только на число.
Частая ошибка
Объявить через const значение, которое потом меняется целиком, например счётчик:
const attempts = 0;
attempts = attempts + 1; // ✗error TS2588: Cannot assign to 'attempts' because it is a constant.attempts + 1 — новое число, и записать его в имя можно, только если оно объявлено через let. Исправление — let attempts = 0;. Правило выбора то же, что в JavaScript: const по умолчанию, let — когда имени дадут новое значение.
Главное
constзапрещает дать имени новое значение:cart = …— ошибкаTS2588.- Содержимое объекта и массива в
constменять можно: свойства, элементы,push. - Типы свойств выводятся общими: в
cart.totalтипаnumberможно записать другое число, но не строку. - Для значения, которое меняется целиком, например для счётчика, нужен
let.