Проверка типов в процессе сборки и вне неё

TypeScript в связке с Rollup не ограничивается простой транспиляцией .ts в .js. При корректной архитектуре сборки проверка типов может быть вынесена в отдельный этап или, наоборот, интегрирована в пайплайн сборки таким образом, чтобы балансировать между скоростью и безопасностью кода. Это особенно важно в библиотеках и крупных фронтенд-проектах, где стоимость ошибки в типах выше стоимости дополнительного шага сборки.

TypeScript-компилятор выполняет две функции одновременно: транспиляцию кода и проверку типов. Rollup, в свою очередь, занимается модульной сборкой и оптимизацией. При попытке объединить эти процессы напрямую возникает конфликт интересов: Rollup ориентирован на скорость и потоковую обработку модулей, тогда как проверка типов может быть относительно тяжёлой операцией.

Поэтому на практике различают два режима работы:

  • проверка типов во время сборки (synchronous type checking)
  • проверка типов вне процесса сборки (asynchronous / detached type checking)

Каждый из подходов решает разные задачи и влияет на архитектуру проекта.

Встроенная проверка типов через @rollup/plugin-typescript

Один из наиболее прямых способов интеграции TypeScript в Rollup — использование плагина @rollup/plugin-typescript. Он вызывает TypeScript API и выполняет компиляцию вместе с базовой проверкой типов.

Однако важно понимать ключевую особенность: в зависимости от конфигурации, проверка типов может быть частично или полностью отключена в пользу ускорения сборки.

Типичный пример конфигурации:

import typescript from '@rollup/plugin-typescript';

export default {
  input: 'src/index.ts',
  output: {
    dir: 'dist',
    format: 'esm'
  },
  plugins: [
    typescript({
      tsconfig: './tsconfig.json'
    })
  ]
};

В таком режиме:

  • TypeScript участвует в сборке Rollup
  • базовые ошибки типов могут быть обнаружены
  • скорость сборки снижается при больших проектах

Ключевая проблема заключается в том, что Rollup остаётся синхронным инструментом, а полноценная проверка типов требует обхода всего графа зависимостей.

Ограничения встроенной проверки типов

При использовании только @rollup/plugin-typescript возникают следующие ограничения:

1. Частичная проверка типов

Некоторые конфигурации плагина оптимизируют производительность за счёт отключения полной type-check стадии. В этом случае происходит фактически транспиляция с минимальной проверкой.

2. Замедление сборки

Полная проверка типов увеличивает время сборки пропорционально размеру проекта. При росте кодовой базы это становится критическим фактором.

3. Отсутствие параллелизма

Rollup не разделяет процессы трансформации и проверки типов. Это приводит к блокировке сборочного процесса.

Эти ограничения приводят к тому, что в большинстве production-сценариев проверка типов выносится за пределы Rollup.

Вынос проверки типов в отдельный процесс

Наиболее распространённая архитектура — разделение сборки и проверки типов.

Rollup отвечает за:

  • трансформацию модулей
  • tree-shaking
  • генерацию бандла

TypeScript (tsc) отвечает за:

  • строгую проверку типов
  • анализ зависимостей типов
  • контроль интерфейсов и контрактов

Команда проверки типов выполняется отдельно:

tsc --noEmit

Флаг --noEmit гарантирует, что TypeScript не будет генерировать JavaScript, а выполнит только анализ типов.

Преимущества подхода

  • ускорение сборки Rollup
  • возможность параллельного запуска процессов
  • более строгая и полная проверка типов
  • независимость от плагинов Rollup

Недостатки

  • отсутствие мгновенной обратной связи в сборке
  • необходимость координации скриптов сборки
  • риск рассинхронизации конфигураций TypeScript и Rollup

Параллельная модель сборки и type-checking

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

  • Rollup запускается в режиме watch или build
  • TypeScript запускается как отдельный процесс watch

Пример скриптов:

{
  "scripts": {
    "build": "rollup -c",
    "typecheck": "tsc --noEmit",
    "dev": "concurrently \"rollup -c -w\" \"tsc --noEmit -w\""
  }
}

Такая схема позволяет:

  • получать быстрые пересборки через Rollup
  • одновременно видеть ошибки типов в отдельном процессе
  • не блокировать сборку при ошибках TypeScript

Использование TypeScript Project References

При больших кодовых базах применяются project references. Это механизм, позволяющий разделять TypeScript-проект на подмодули с независимой компиляцией и проверкой типов.

Структура:

packages/
  core/
  ui/
  app/

Каждый пакет имеет собственный tsconfig.json и может ссылаться на другие пакеты.

Пример конфигурации:

{
  "compilerOptions": {
    "composite": true
  },
  "references": [
    { "path": "../core" },
    { "path": "../ui" }
  ]
}

В таком режиме:

  • проверка типов становится инкрементальной
  • TypeScript кэширует результаты
  • ускоряется общий type-check процесс

Rollup при этом продолжает работать поверх уже собранных или резолвленных модулей.

Инкрементальная проверка типов

TypeScript поддерживает инкрементальную проверку через tsBuildInfo:

{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": ".tsbuildinfo"
  }
}

Это особенно эффективно при вынесенной проверке типов:

  • повторные проверки затрагивают только изменённые файлы
  • сокращается время tsc --noEmit
  • повышается эффективность watch-режима

Разделение ответственности: Rollup vs TypeScript

Архитектурно важно разграничить зоны ответственности:

Rollup

  • объединение модулей
  • оптимизация
  • tree-shaking
  • работа с плагинами (Babel, PostCSS, resolve)

TypeScript

  • статическая типизация
  • проверка контрактов
  • анализ интерфейсов
  • контроль корректности импортов на уровне типов

Попытка заставить Rollup полностью заменить tsc приводит к потере строгой типовой гарантии.

Проверка типов в CI/CD

В CI пайплайнах проверка типов почти всегда выполняется отдельно от сборки.

Типичный сценарий:

  1. установка зависимостей
  2. tsc --noEmit
  3. rollup -c

Иногда порядок меняется, но чаще проверка типов выполняется первой, чтобы быстро отбраковать некорректные коммиты.

Дополнительно применяются:

  • кэширование node_modules
  • кэширование .tsbuildinfo
  • параллельные job’ы для type-check и build

Типизация Rollup-конфигураций

Дополнительный слой проверки типов возникает внутри самой конфигурации Rollup. Конфигурационный файл может быть написан на TypeScript:

import { RollupOptions } from 'rollup';

const config: RollupOptions = {
  input: 'src/index.ts',
  output: {
    dir: 'dist',
    format: 'esm'
  }
};

export default config;

В этом случае:

  • TypeScript проверяет корректность Rollup API
  • исключаются ошибки конфигурации
  • повышается надёжность сборочного процесса

Однако этот слой не заменяет проверку типов исходного кода.

Ошибки типов как часть сборочного пайплайна

В некоторых командах применяется строгий режим, при котором сборка Rollup блокируется при наличии ошибок типов.

Это достигается через объединение команд:

tsc --noEmit && rollup -c

В таком подходе:

  • Rollup запускается только при успешной проверке типов
  • исключается попадание некорректного кода в бандл
  • сборка становится детерминированной

Минусом является увеличение времени ожидания при разработке.

Разделение dev и production стратегий

Часто используются разные стратегии:

Development

  • Rollup в watch режиме
  • tsc --noEmit -w отдельно
  • допускаются частичные ошибки до сохранения

Production

  • строгая проверка типов
  • блокировка сборки при ошибках
  • отсутствие watch режима

Такое разделение позволяет балансировать между скоростью разработки и качеством релиза.

Роль ESLint в проверке типов

Дополнительно к TypeScript часто подключается typescript-eslint. Он выполняет частичную семантическую проверку кода.

Однако важно учитывать:

  • ESLint не заменяет tsc
  • ESLint работает быстрее, но менее полно
  • дублирование правил требует аккуратной настройки

В связке с Rollup ESLint обычно выполняется до сборки или параллельно с type-check процессом.

Итоговая архитектурная модель проверки типов

На практике устойчивые системы используют следующую модель:

  • Rollup: сборка и оптимизация
  • TypeScript (tsc --noEmit): полная проверка типов
  • ESLint: быстрый анализ и стиль
  • CI: строгая последовательность проверок
  • Dev: параллельные процессы с watch режимом

Такое разделение обеспечивает баланс между скоростью сборки и строгой гарантией типовой корректности кода, не перегружая Rollup задачами, для которых он не предназначен.