as const
Помните запечатанную банку const? На её наклейке TypeScript пишет точное содержимое: const limit = 5 получает литеральный тип 5. С массивом так не выходит: даже в const его элементы получают общий тип.
Посмотрим, где это мешает. Функция принимает только размеры из фиксированного списка, а размеры лежат в массиве:
type Size = "S" | "M" | "L";
function order(size: Size) {
console.log(size);
}
const sizes = ["S", "M", "L"];
order(sizes[0]); // ✗error TS2345: Argument of type 'string' is not assignable to parameter of type 'Size'.У sizes тип string[]. Массив в const можно менять (запись sizes[0] = "XXL" разрешена), поэтому TypeScript расширяет "S", "M" и "L" до string, как у let. А string к параметру Size не подходит.
Как работает as const
Запись as const после литерала говорит TypeScript: это значение не изменится, опиши его точно. Добавим её к массиву:
type Size = "S" | "M" | "L";
function order(size: Size) {
console.log(size);
}
const sizes = ["S", "M", "L"] as const;
order(sizes[0]); // SТеперь у sizes тип readonly ["S", "M", "L"], и вызов компилируется. В этом типе сразу три изменения:
- литеральные типы сохранились: на месте 0 стоит
"S", а неstring; - массив стал кортежем: в нём ровно три места, и у каждого свой тип;
- всё стало только для чтения: ни
sizes.push("XL"), ниsizes[0] = "M"tsc не пропустит.
На push компилятор отвечает так же, как у любого массива только для чтения:
error TS2339: Property 'push' does not exist on type 'readonly ["S", "M", "L"]'.Это как ряд шкафчиков, запаянный на заводе: на каждой дверце написано точное содержимое, и ни одну не открыть.
as const у объекта
as const работает и с объектным литералом:
const config = { mode: "dark", retries: 3 } as const;
console.log(config.mode, config.retries); // dark 3Без as const у config был бы тип { mode: string; retries: number; }. С ним — { readonly mode: "dark"; readonly retries: 3; }:
- каждое свойство стало
readonly, иconfig.retries = 4даст ошибкуTS2540; - у каждого свойства литеральный тип:
"dark"и3.
as const действует на всю глубину. У { sizes: ["S", "M"] } as const тип { readonly sizes: readonly ["S", "M"]; }: только для чтения и свойство, и массив внутри него. Модификатор readonly у свойства так не умеет: он защищает один уровень.
Здесь аналогия с запаянными шкафчиками перестаёт работать. Запаяно только для tsc: as const стирается, и в JavaScript-файле остаётся const sizes = ["S", "M", "L"]; — обычный массив, который при запуске можно изменить.
Когда пригодится as const
as const нужен там, где набор значений известен заранее и не меняется:
- список вариантов для выбора: размеры, роли, пункты меню;
- настройки, которые программа только читает;
- значения, которые потом передают в параметры литеральных типов, как в
order(sizes[0]).
Есть и другой путь — аннотация. С const sizes: Size[] = ["S", "M", "L"]; вызов order(sizes[0]) тоже скомпилируется. Но это обычный массив: его можно менять, и длина у него любая. as const закрепляет и значения, и длину.
Массив as const — только для чтения, поэтому в параметр типа string[] его не передать. У такого параметра пишут readonly string[].
На собеседовании любят спрашивать, чем const отличается от as const. const запрещает записать в имя новое значение. as const описывает само значение: сохраняет его литералы и запрещает менять его содержимое.
Частая ошибка
as const дописали к переменной, а не к литералу:
const sizes = ["S", "M", "L"];
const fixed = sizes as const; // ✗error TS1355: A 'const' assertion can only be applied to references to enum members, or string, number, boolean, array, or object literals.tsc отвечает, что as const применяют только к литералам — строковым, числовым, логическим, к литералам массива и объекта — и к членам enum. Ко второй строке тип sizes уже выведен как string[], и литералы "S", "M", "L" из него не вернуть. Пишите as const сразу после литерала: const sizes = ["S", "M", "L"] as const;.
Главное
as constпосле литерала сохраняет литеральные типы и делает значение только для чтения.- Массив с
as constстановится кортежем только для чтения:["S", "M"] as const— этоreadonly ["S", "M"]. - У объекта с
as constвсе свойстваreadonlyи литеральных типов, на всю глубину. as constпишут у самого литерала, и проверяет его только tsc.