«Could not find a declaration file»: как подружить TypeScript с любым JS-модулем

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

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

Начать курс

«Невидимая» ошибка, которая знакома каждому TypeScript-разработчику

Вы пишете код на TypeScript, всё идёт гладко, но вдруг — красная волнистая линия под импортом. Компилятор сообщает нечто пугающее: Could not find a declaration file for module 'module-name'. '/path/to/module-name.js' implicitly has an 'any' type. Если вы хоть раз работали с TypeScript, вы наверняка встречали это сообщение. Оно появляется, когда вы пытаетесь импортировать JavaScript-модуль, для которого не существует файла деклараций (обычно с расширением .d.ts).

На Stack Overflow этот вопрос набрал тысячи просмотров. И это не случайно: проблема кажется сложной, хотя на самом деле имеет несколько простых и элегантных решений. В этой статье мы разберём, почему возникает ошибка, и сравним четыре основных подхода к её устранению. Вы узнаете, какой способ выбрать в зависимости от ситуации, и сможете навсегда забыть о «невидимых» типах.

Почему TypeScript требует декларации?

TypeScript — это надстройка над JavaScript, которая добавляет статическую типизацию. Когда вы пишете import * as _ from 'lodash', TypeScript должен знать, какие типы имеет переменная _. Если для модуля нет .d.ts файла, компилятор не может проверить типы, и по умолчанию присваивает модулю тип any. Это нарушает строгость типизации, поэтому TypeScript предупреждает вас. На самом деле, это не ошибка выполнения, а предупреждение компиляции, но в строгих проектах (strict: true) оно может блокировать сборку.

Теперь давайте перейдём к решениям. Мы рассмотрим четыре подхода, от самого простого до наиболее гибкого.

Решение 1: Быстрое и грязное — declare module

Самый простой способ — создать файл декларации вручную. Для этого добавьте в свой проект файл global.d.ts (или types.d.ts) и напишите:

// global.d.ts
declare module 'module-name';

Эта строка говорит TypeScript: «Модуль существует, но его тип — any». Проблема решена, но вы теряете все преимущества типизации. Этот подход хорош для быстрых прототипов или когда модуль используется в одном-двух местах. Однако для production-проектов это не лучший выбор.

  • Плюсы: Минимум кода, не требует установки пакетов.
  • Минусы: Тип any убивает смысл TypeScript; если модуль обновится, вы не узнаете об изменении API.

Решение 2: Использование @types пакетов

Для большинства популярных npm-модулей существуют отдельные пакеты с декларациями. Например, для lodash это @types/lodash. Установите их через npm или yarn:

npm install --save-dev @types/lodash

После этого TypeScript автоматически найдёт декларации, и ошибка исчезнет. Если вы используете TypeScript 2.0+, он сам ищет @types папку. Это стандартный и рекомендуемый способ.

  • Плюсы: Полная типизация, поддержка сообщества, автоматические обновления.
  • Минусы: Не для всех модулей есть @types (например, для малоизвестных библиотек).

Как проверить, есть ли типы? Зайдите на npm-страницу или выполните npm info @types/имя-модуля. Если пакет не найден, переходите к следующему решению.

Решение 3: Включение allowJs и checkJs

Если вы пишете проект, который смешивает TypeScript и JavaScript, можно разрешить компилятору проверять JS-файлы. Для этого в tsconfig.json добавьте:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false
  }
}

Опция allowJs позволяет импортировать JS-модули в TS-файлы. При этом TypeScript будет пытаться вывести типы из JS-кода (через JSDoc или анализ). Если checkJs установлен в false, ошибки типов в JS-файлах игнорируются. Это компромисс: вы не получаете полной типизации, но ошибка «implicitly has an any type» исчезает.

  • Плюсы: Не нужно создавать декларации; работает для любых JS-модулей.
  • Минусы: Типизация остаётся неполной; возможны скрытые ошибки.

Решение 4: Радикальное — skipLibCheck

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

{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

Это заставит TypeScript пропускать проверку всех .d.ts файлов. Ошибка исчезнет, но вы рискуете пропустить реальные несоответствия типов. Используйте этот подход только как временное решение или для ускорения сборки.

  • Плюсы: Мгновенное решение; ускоряет компиляцию.
  • Минусы: Полностью отключает проверку типов для библиотек; может маскировать баги.

Сравнение подходов: какой выбрать?

Чтобы вам было проще ориентироваться, мы свели все методы в таблицу. Выбирайте в зависимости от вашего сценария.

Подход Когда использовать Уровень типизации Сложность
declare module Быстрый прототип, одноразовый скрипт Низкий (any) Низкая
@types Популярные библиотеки Высокий Низкая
allowJs Смешанные проекты TS+JS Средний Средняя
skipLibCheck Ускорение сборки, временное решение Отсутствует для библиотек Низкая

На практике большинство разработчиков начинают с declare module, а затем переходят на @types. Если @types нет, можно написать свою декларацию (это тема для отдельной статьи).

Пример из реального проекта

Блоги

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