Хотите заказать веб-сайт? Связаться с нами

Компилятор tsc: входные и выходные файлы

В предыдущем уроке мы узнали, что происходит внутри компилятора в целом: парсинг, проверка типов, стирание аннотаций и эмит JavaScript. Сегодня сфокусируемся на двух конкретных вопросах: какие файлы tsc вообще готов читать, и что именно он создаёт на выходе — а заодно разберём флаги --noEmit, --checkJs и --strict, которые эти границы настраивают.

Компилятор tsc: входные и выходные файлы

Подготовка

Создайте два файла. Первый — обычный TypeScript-файл app.ts:

function getLength(value) {
  return value.length;
}
 
console.log(getLength("TypeScript"));

Второй — уже существующий, «legacy» JavaScript-файл legacy.js:

function add(a = 0, b = 0) {
  return a + b;
}
 
console.log(add("5", 10));

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

Какие файлы видит tsc

Шаг 1: По умолчанию — только .ts и .tsx

Без дополнительных флагов TypeScript ориентирован исключительно на .ts и .tsx. Файлы с расширением .js в компиляцию проекта не включаются — компилятор их просто не видит.

Шаг 2: Подключаем .js через allowJs и checkJs

Чтобы TypeScript начал заглядывать в JavaScript-файлы, нужны два флага: --allowJs разрешает включать .js-файлы в компиляцию, а --checkJs заставляет их ещё и проверять на ошибки типов — по сути, это то же самое, что добавить // @ts-check в начало файла. Попробуйте:

npx tsc legacy.js --allowJs --checkJs

Вместо ожидаемой ошибки типов вы увидите совсем другую:

error TS5055: Cannot write file 'legacy.js' because it would overwrite input file.

Компилятор пытается не только проверить файл, но и по привычке скомпилировать его — а результат хочет записать в тот же legacy.js, который читает как исходник. Отсюда и следующий флаг.

Куда деваются файлы на выходе

Шаг 3: Обычная компиляция создаёт .js

Скомпилируйте app.ts обычным способом:

npx tsc app.ts

Рядом появится файл app.js — это стандартное поведение tsc: читать .ts, создавать .js.

Шаг 4: —noEmit — только проверка, без файлов

Теперь вернёмся к legacy.js и добавим флаг, который решает проблему из шага 2 — он запрещает компилятору создавать какие-либо файлы вообще, оставляя только проверку типов:

npx tsc legacy.js --allowJs --checkJs --noEmit

На этот раз ошибка именно та, которую мы ожидали:

legacy.js:5:18 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
 
5 console.log(add("5", 10));
                 ~~~
 
Found 1 error in legacy.js:5

Так как оба параметра a и b имеют значения по умолчанию 0, TypeScript вывел для них тип number — и передача строки "5" сразу стала ошибкой.

Если проект уже собирается через Vite, esbuild или webpack, не заставляйте tsc создавать файлы вообще. Вынесите проверку типов в отдельный npm-скрипт, например "typecheck": "tsc --noEmit", и запускайте его в CI — сборкой пусть занимается основной бандлер.

Ужесточаем проверку: флаг --strict

Проверьте app.ts без дополнительных флагов:

npx tsc app.ts --noEmit

Ошибок нет, хотя параметр value в функции getLength вообще не типизирован. Добавьте --strict:

npx tsc app.ts --noEmit --strict

Теперь компилятор возражает:

app.ts:1:21 - error TS7006: Parameter 'value' implicitly has an 'any' type.
 
1 function getLength(value) {
                      ~~~~~
 
Found 1 error in app.ts:1

--strict — это не один флаг, а целый набор более строгих проверок, включённых разом. Вот некоторые из них:

Флаг Что проверяет
noImplicitAny Запрещает неявный тип any у параметров и переменных
strictNullChecks Требует явно учитывать null и undefined
strictFunctionTypes Строже проверяет совместимость типов функций
strictPropertyInitialization Требует инициализировать свойства класса

Запуск и проверка

Пройдите обе проверки ещё раз, чтобы закрепить результат:

npx tsc app.ts --noEmit --strict
npx tsc legacy.js --allowJs --checkJs --noEmit

Первая команда должна вывести ошибку про неявный any, вторая — ошибку про несовместимый тип аргумента.

Бонус: удобные комбинации флагов

  1. tsc --noEmit --strict — быстрая проверка проекта на CI с максимальной строгостью, без создания файлов.
  2. tsc --allowJs --checkJs --noEmit — аудит существующего JavaScript-кода без переименования файлов в .ts.
  3. Добавьте оба варианта в package.json как отдельные npm-скрипты, чтобы не запоминать флаги каждый раз.

Не обязательно сразу переименовывать все .js-файлы в .ts, чтобы начать получать пользу от TypeScript. Включите allowJs и checkJs в tsconfig.json — и компилятор начнёт проверять существующий JavaScript уже сегодня, без единой миграции.

Итог

В этом уроке мы разобрались, как настраивать вход и выход компилятора:

  1. По умолчанию tsc читает только .ts и .tsx — для .js нужны --allowJs и --checkJs.
  2. Если входной и выходной файл совпадают, компилятор отказывается их перезаписывать — на помощь приходит --noEmit.
  3. --noEmit удобен, когда проверка типов не должна создавать файлы — например, в CI или рядом со сторонним бандлером.
  4. --strict включает набор более строгих проверок разом, включая запрет неявного any.

В следующем уроке разберём ещё один важный параметр компиляции — target, который определяет, под какую версию JavaScript адаптируется ваш код.

Теги: