as const и const assertions
as const(const assertion) — постфиксный оператор TypeScript, который говорит компилятору: «трактуй это выражение как максимально узкий литеральный тип и сделай все свойства readonly», превращая"hello"изstringв"hello"и объект из мутабельного вreadonly.
Зачем нужно
По умолчанию TypeScript расширяет типы: let x = "north" даёт string, а не "north". as const фиксирует литеральный тип, что критично для discriminated unions, enum-замен и корректной работы typeof arr[number] для извлечения union типов.
Где используется
- Замена
enum—as constобъект +typeofunion - Фиксация конфигурационных объектов как неизменяемых
- Кортежи с точными типами вместо
(string | number) - Константы маршрутов, событий, ключей конфигурации
Основной контент
Примитивы
let x = "north"; // x: string (widened)
const y = "north"; // y: "north" (literal — const сужает)
let z = "north" as const; // z: "north" (literal — явно сужено)
Объекты
// Без as const — типы полей широкие
const config = {
host: "localhost",
port: 3000,
debug: true,
};
// { host: string; port: number; debug: boolean }
// С as const — типы полей — литералы, поля readonly
const config = {
host: "localhost",
port: 3000,
debug: true,
} as const;
// { readonly host: "localhost"; readonly port: 3000; readonly debug: true }
// config.host = "example.com"; // Error — readonly
Массивы и кортежи
// Без as const — тип: string
const directions = ["north", "south", "east", "west"];
// directions: string
// С as const — тип: readonly ["north", "south", "east", "west"]
const directions = ["north", "south", "east", "west"] as const;
// directions: readonly ["north", "south", "east", "west"]
// Извлечение union типов
type Direction = typeof directions[number];
// "north" | "south" | "east" | "west"
Замена enum через as const
const Status = {
Pending: "PENDING",
Active: "ACTIVE",
Inactive: "INACTIVE",
} as const;
type Status = typeof Status[keyof typeof Status];
// "PENDING" | "ACTIVE" | "INACTIVE"
function setStatus(s: Status): void { /* ... */ }
setStatus(Status.Active); // OK
setStatus("ACTIVE"); // OK — строка совместима с "ACTIVE"
setStatus("unknown"); // Error
as const в функциях
// Функция возвращает кортеж, но TypeScript по умолчанию даёт массив
function getCoords() {
return [55.75, 37.62]; // number — не [number, number]!
}
// Исправление:
function getCoords() {
return [55.75, 37.62] as const; // readonly [55.75, 37.62]
}
// Или явная аннотация:
function getCoords: [number, number] {
return [55.75, 37.62];
}
Объединение as const с другими типами
// Частичное применение — только нужные поля
const partialConfig = {
host: "localhost" as const, // только host — литерал
port: 3000, // port остаётся number
};
// { host: "localhost"; port: number }
Частые ошибки
as constнаletпеременной — можно, ноletвсё равно позволяет переприсвоение переменной; сама переменная не readonly.- Ожидать глубокое замораживание в рантайме —
as constтолько на уровне типов; для рантайм-защиты нуженObject.freeze. typeof obj[keyof typeof obj]безas const— безas constдастstring, а не union литералов.- Применять к динамическим значениям —
as constтребует что значение было литералом на момент компиляции; переменная сas constне работает какas const.