TypeScript / 38 минут

Модули, tsconfig и сборка

Настройте strict-проверку, модули, декларации и разделение типов и значений.

В процессе Теория: впереди Практика: впереди Тест: впереди
12 / 12
После этой главы
  • Настройте strict-проверку, модули, декларации и разделение типов и значений.
  • Применить тему в задаче «Модули, tsconfig и сборка» и объяснить граничные случаи.

Около 8 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Проверка и выпуск JavaScript — разные стадии

TypeScript-проект нуждается не только в .ts-файлах, но и в согласованной конфигурации проверки и запуска. Компилятор может проверять типы, преобразовывать синтаксис и генерировать JavaScript; сборщик иногда выполняет часть этих задач отдельно. Если инструмент только удаляет аннотации, это ещё не означает полноценного typecheck. В проекте должен быть явный обязательный шаг проверки.

Команда tsc --noEmit позволяет проверить код без выпуска файлов. В CI её удобно отделить от сборки приложения, чтобы ошибка типов не скрылась за успешной трансформацией. Конкретный фреймворк может предоставлять собственную команду, которая дополнительно учитывает компоненты и генерируемые типы. Главное — знать, какая проверка действительно запускается и с какой конфигурацией.

Базовые настройки strict

strict включает семейство более строгих проверок. Он помогает не забыть неявный any и отсутствие значения, но не делает внешний ввод проверенным автоматически. Опции noUncheckedIndexedAccess и exactOptionalPropertyTypes полезно рассматривать отдельно: не каждая строгая настройка входит в одно и то же семейство. Не включайте или отключайте параметры случайным набором без понимания их влияния на контракт.

JSON
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "noEmitOnError": true
  }
}

Это пример для подходящего Node.js-проекта, а не универсальная конфигурация для любого сборщика или Nuxt. Фреймворк может генерировать базовый tsconfig и требовать его расширения. Нельзя бездумно заменить его отдельным минимальным файлом и ожидать сохранения всех путей, типов окружения и настроек компонентов.

target, lib и доступность API

target определяет выпуск определённых синтаксических конструкций, а lib участвует в описании доступных стандартных API. Они не устанавливают runtime-полифиллы автоматически. Успешная проверка вызова нового метода массива не гарантирует, что старый браузер реально содержит его реализацию. Целевую среду и стратегию совместимости нужно задавать отдельно.

TypeScript
const values: number[] = [3, 1, 2];
const sorted = values.toSorted((a, b) => a - b);

Такой код требует соответствующего API у выполняющей среды или подходящего полифилла. Аналогично DOM-типы не создают настоящий document в Node.js. Разрешённые компилятором имена должны соответствовать месту запуска, а не просто присутствовать в широком наборе глобальных деклараций.

Формат модулей и import type

ESM и CommonJS имеют разные способы загрузки и экспорта. TypeScript должен согласовать module и moduleResolution с реальной средой или сборщиком. Поле type в package.json, расширения файлов и правила specifier влияют на то, какой код получится и как он будет выполняться. Настройка path alias не всегда переписывает runtime-путь сама по себе.

TypeScript
import { posix } from 'node:path';
import type { Stats } from 'node:fs';
const filename = posix.basename('/courses/intro.ts');
type FileInfo = Pick<Stats, 'size'>;

posix нужен как реальное значение, а Stats используется только для типа. import type явно отделяет второй случай и не должен применяться к функции, которую вы собираетесь вызвать. Различие особенно важно при настройках сохранения синтаксиса модулей и при работе с зависимостями, имеющими побочные эффекты загрузки.

Декларации и границы зависимостей

.d.ts описывает API существующего JavaScript, но не содержит его реализацию для запуска. Пакет может поставлять декларации сам или получать их из @types. Если типы не соответствуют установленной runtime-версии, автодополнение может обещать несуществующий метод. Фиксируйте зависимости lockfile и проверяйте совместимость версий, а не ориентируйтесь только на найденный пример.

TypeScript
declare function formatTitle(value: string): string;
// Декларация сообщает тип, но не создаёт runtime-функцию.

skipLibCheck может уменьшить стоимость проверки деклараций зависимостей, но не заменяет проверку собственного кода и не доказывает совместимость всех библиотек. Аналогично suppress-утверждения и @ts-ignore следует использовать только с понятной локальной причиной и планом проверки, а не как универсальное решение любой ошибки сборки.

Практика и итоговый TypeScript-проект

Задание использует обычный import значения posix и типизированный массив путей. Решение компилируется в строгом режиме внутри песочницы. Вы должны вернуть basename каждого пути, сохранив порядок и пустой массив. Это проверяет связь аннотаций, модульного импорта и реального выполнения, а не только красивую сигнатуру.

Итоговый проект курса — типизированный API-клиент. Разделите транспортный запрос, проверку unknown, модель результата и отображение ошибки. Добавьте отмену устаревшего запроса, тест повреждённого ответа и обязательный typecheck. Готовность проекта означает согласованность статических контрактов с runtime-поведением, а не отсутствие красных подчёркиваний в одном открытом файле редактора.

Загружаем редактор…
Ты дошёл до конца разделаЗакрепить знания в проекте

Музыка: Reverie — Scott Buckley · CC BY 4.0