Type 'string' is not assignable to type: как понять и исправить ошибку TypeScript

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

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

Начать курс

«Type 'string' is not assignable to type» — что это и почему вы здесь?

Если вы когда-нибудь писали на TypeScript, то наверняка сталкивались с красным подчеркиванием в редакторе кода и сообщением:

Type 'string' is not assignable to type 'SomeType'.

Эта ошибка — настоящий «привет» от строгой типизации. Она пугает новичков, но на самом деле является лучшим другом разработчика. В этой статье мы разберём, почему возникает ошибка Type 'string' is not assignable to type, как её исправить и какие подходы существуют для работы со строковыми литералами, union-типами и дженериками. Статья написана для образовательного сайта pythonlib.ru, но примеры полностью на TypeScript.

1. Корень проблемы: почему TypeScript ругается на строки?

TypeScript — это надстройка над JavaScript, которая добавляет статическую типизацию. Когда вы пишете:

let role: 'admin' | 'user' = 'admin';
role = 'moderator'; // Ошибка: Type '"moderator"' is not assignable to type '"admin" | "user"'

TypeScript понимает, что переменная role может принимать только два значения: 'admin' или 'user'. Когда вы пытаетесь присвоить 'moderator', компилятор выдаёт ошибку. Но почему он пишет «Type 'string' is not assignable to type»?

Дело в том, что строковый литерал 'moderator' имеет тип string, а ожидаемый тип — 'admin' | 'user'. TypeScript сравнивает типы, а не значения. Он видит: «Ты даёшь мне обычную строку, а я жду конкретные строки». Отсюда и сообщение.

1.1. Простой пример для понимания

Рассмотрим базовый случай:

type Status = 'active' | 'inactive';

function updateStatus(status: Status) {
  console.log(status);
}

const myStatus = 'active';
updateStatus(myStatus); // OK

const anotherStatus = 'pending';
updateStatus(anotherStatus); // Ошибка: Type 'string' is not assignable to type 'Status'

В последнем случае переменная anotherStatus выводится как string, а не как 'pending'. TypeScript не знает, что 'pending' — это литерал. Он видит только тип string. Исправить это можно, явно указав тип:

const anotherStatus: 'pending' = 'pending'; // Теперь работает

2. Сравнение подходов: как обойти или исправить ошибку

Существует несколько способов справиться с ошибкой Type 'string' is not assignable to type. Мы сравним их по простоте, безопасности и производительности.

2.1. Подход 1: as const — заморозка литерала

Самый элегантный способ — использовать as const. Он «замораживает» значение как литерал:

const myStatus = 'active' as const;
updateStatus(myStatus); // OK

Плюсы: просто, читаемо, безопасно.
Минусы: работает только с const, не с let.

2.2. Подход 2: Type Assertion (as) — явное приведение

Вы можете сказать TypeScript: «Я знаю, что делаю»:

const someString: string = 'active';
updateStatus(someString as Status); // OK

Плюсы: быстрое решение.
Минусы: вы отключаете проверку типов. Если передадите неверное значение, ошибка проявится только в рантайме.

2.3. Подход 3: Type Guard (защитник типа)

Напишем функцию, которая проверяет, является ли строка допустимым статусом:

function isStatus(value: string): value is Status {
  return value === 'active' || value === 'inactive';
}

function safeUpdate(value: string) {
  if (isStatus(value)) {
    updateStatus(value); // TypeScript знает, что value — Status
  } else {
    throw new Error('Недопустимый статус');
  }
}

Плюсы: максимальная безопасность, рантайм-проверка.
Минусы: больше кода, нужно писать отдельную функцию.

2.4. Подход 4: Дженерики и infer

Для сложных случаев можно использовать дженерики:

function getStatus<T extends string>(value: T): T extends Status ? T : never {
  if (value === 'active' || value === 'inactive') {
    return value as any;
  }
  throw new Error('Invalid');
}

Это продвинутый приём для библиотек и API. В повседневной практике он избыточен.

3. Реальные сценарии из жизни: когда ошибка «Type 'string' is not assignable to type» встречается чаще всего

  • Работа с API: бэкенд присылает строку, а фронтенд ожидает union-тип.
  • Массивы: ['active', 'inactive'] выводится как string[], а не массив литералов.
  • Enum-подобные объекты: попытка использовать Object.values() с union-типом.

3.1. Пример с массивом

Частая ошибка:

const statuses = ['active', 'inactive'];
// statuses имеет тип string[]

statuses.forEach(status => updateStatus(status)); // Ошибка!

Исправление с as const:

const statuses = ['active', 'inactive'] as const;
// statuses имеет тип readonly ["active", "inactive"]

statuses.forEach(status => updateStatus(status)); // OK

4. Сравнительная таблица подходов

Для наглядности сведём все методы в таблицу:

Подход Сложность Безопасность Гибкость
as const Низкая Высокая Средняя
Type Assertion Низкая Низкая Высокая
Type Guard Средняя Очень высокая Высокая
Дженерики Высокая Высокая Очень высокая

5. Как избежать ошибки в будущем: best practices

Вот несколько советов, которые помогут вам реже видеть Type 'string' is not assignable to type: