«as const» в TypeScript: спасаем код от глупых ошибок

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему ваш TypeScript-код ведёт себя странно?

Вы когда-нибудь писали в TypeScript что-то вроде:

const direction = 'left';
// ... а потом TypeScript говорил, что это string, а не 'left'?

Знакомо? Это одна из самых частых ловушек, в которую попадают даже опытные разработчики. Сегодня мы разберём мощный инструмент, который решает эту проблему раз и навсегда — as const.

Тема этой статьи основана на реальном вопросе с Stack Overflow, который собрал тысячи просмотров: "What does the "as const" mean in TypeScript and what is its use case?". Мы адаптируем этот вопрос для образовательного контента, добавим живые примеры и покажем, как as const в TypeScript помогает избежать глупых ошибок.

Готовы? Поехали!


Что такое as const в TypeScript простыми словами?

as const — это специальная конструкция (const assertion), которая говорит TypeScript: «Эй, не расширяй тип! Заморозь это значение как литерал». Другими словами, вы запрещаете TypeScript выводить более общий тип (например, string) и заставляете его использовать точное значение.

Проблема без as const

Посмотрите на простой пример:

// Без as const
const role = 'admin'; // TypeScript выведет тип: 'admin' (потому что const)
let userRole = 'admin'; // TypeScript выведет тип: string (потому что let)

// А теперь массив:
const roles = ['admin', 'user', 'guest']; 
// TypeScript выведет: string[]

Когда вы используете let или массив, TypeScript «расширяет» тип до более общего. Это логично: переменная может измениться, массив может мутировать. Но в 90% случаев вам нужно именно точное значение!

Решение с as const

const roles = ['admin', 'user', 'guest'] as const;
// TypeScript выведет: readonly ["admin", "user", "guest"]

const config = {
  api: 'https://api.example.com',
  timeout: 5000
} as const;
// TypeScript выведет:
// {
//   readonly api: "https://api.example.com";
//   readonly timeout: 5000;
// }

Теперь TypeScript знает точные литералы. Никаких string или number — только конкретные значения. Это и есть магия as const в TypeScript.


Топ-5 ошибок при работе с as const (и как их избежать)

Даже зная, что такое as const, разработчики часто допускают одни и те же ошибки. Давайте разберём их на реальных кейсах.

Ошибка №1: Попытка изменить readonly-объект

Когда вы используете as const, объект или массив становятся readonly. Попытка изменить их приведёт к ошибке компиляции.

Плохо:

const colors = ['red', 'green', 'blue'] as const;
colors.push('yellow'); // Ошибка! Property 'push' does not exist on type 'readonly [...]'

Как избежать:

Если вам нужен мутируемый массив, не используйте as const. Или используйте копию:

const colors = ['red', 'green', 'blue'] as const;
const mutableColors = [...colors]; // Теперь это обычный массив
mutableColors.push('yellow'); // Работает

Ошибка №2: Забыли про вложенные объекты

as const работает рекурсивно — он делает readonly все вложенные свойства. Многие об этом забывают.

Пример:

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d'
  }
} as const;

theme.colors.primary = '#000'; // Ошибка! Cannot assign to 'primary' because it is a read-only property.

Как избежать:

Если вам нужно изменить только часть объекта, делайте поверхностный as const или используйте частичные типы.

Ошибка №3: Сравнение с as const и без

Иногда разработчики путают поведение as const с обычным приведением типов.

Плохо:

const value = 'hello' as const; // Тип: 'hello'
const value2 = 'hello' as string; // Тип: string
// Они не равны!

Как избежать:

Помните: as const — это утверждение константы, а не приведение к базовому типу. Используйте его только когда вам нужна максимальная точность типа.

Ошибка №4: Использование as const с функциями

as const не работает с функциями или классами — только с литералами.

Пример ошибки:

const fn = () => 'hello' as const; // Тип: () => 'hello' — работает
const fn2 = (() => 'hello') as const; // Ошибка! A 'const' assertions can only be applied to...

Как избежать:

Применяйте as const к возвращаемому значению функции, а не к самой функции.

Ошибка №5: Излишнее использование as const

Не нужно использовать as const везде. Это может сделать код излишне строгим и трудным для рефакторинга.

Плохо:

const name = 'John' as const; // Избыточно, const и так даёт литерал

Как избежать:

Используйте as const только для массивов, объектов и случаев, когда TypeScript расширяет тип против вашей воли.


Реальные сценарии использования as const

Теперь, когда мы знаем, как избежать ошибок, давайте посмотрим, где as const в TypeScript действительно незаменим.

Сценарий 1: Строгая типизация конфигурации

const API_ENDPOINTS = {
  users: '/api/users',
  posts: '/api/posts',
  comments: '/api/comments'
} as const;

type Endpoint = typeof API_ENDPOINTS[keyof typeof API_ENDPOINTS];
// Endpoint = "/api/users" | "/api/posts" | "/api/comments"

function fetchData(endpoint: Endpoint) {
  // Только допустимые endpoint'ы
}

Теперь вы не сможете случайно передать '/api/xyz' — TypeScript поймает ошибку на этапе компиляции.

Сценарий 2: Создание enum-подобных структур

В TypeScript есть нативные enum, но они имеют свои недостатки (например, генерируют лишний код). as const — отличная альтернатива.

const Status = {
  PENDING: 'pending',
  SUCCESS: 'success',
  ERROR: 'error'
} as const;

type StatusType = typeof Status[keyof typeof Status];
// StatusType = "pending" | "success                

Блоги

Рекомендаций по книгам