Интеграция с JavaScript

Интеграция с JavaScript

Технический фундамент миграции JS -> TS

У стабильной миграции есть техническая стратегия:

  • типизировать сначала модули с высоким риском и большим числом зависимостей;
  • постепенно ужесточать tsconfig, а не включать максимум строгости сразу;
  • явно типизировать границы между legacy JS и новым TS;
  • держать временные any локальными и отслеживаемыми.

Это позволяет повышать надежность системы без "заморозки" продуктовой разработки.

Почему интеграция важнее "идеальной миграции"

В реальности редко бывает проект, который с нуля полностью на TypeScript. Чаще есть существующий JavaScript-код, и TypeScript внедряют постепенно. Главная задача не "переписать всё за неделю", а безопасно повышать качество без остановки разработки.

Ключевой момент: успешная интеграция TS в JS-проект это поэтапная стратегия, а не одномоментный рефакторинг.

Проверь себя: почему попытка мгновенно типизировать весь legacy-код часто приводит к срыву сроков?

Пошаговый старт

Обычно начинают так:

  1. Добавляют TypeScript в сборку.
  2. Создают tsconfig.json с умеренно строгими настройками.
  3. Переводят новые файлы в .ts/.tsx.
  4. Постепенно типизируют критичные JS-модули.

Это позволяет получать пользу от TS сразу, не ломая текущий процесс.

Работа с существующим JavaScript

TypeScript умеет проверять и .js файлы в режиме checkJs.

// @ts-check

/** @param {number} a @param {number} b */
function sum(a, b) {
  return a + b;
}

Даже без полного перехода на .ts можно добавить базовую типовую дисциплину через JSDoc.

Смотри, что важно: это хороший мост для больших legacy-проектов.

Анти-провал: если нужно временно подавить типовую ошибку, чаще предпочтительнее // @ts-expect-error, чем // @ts-ignore. Первая конструкция заставит тебя вернуться и убрать "костыль", когда ошибка исчезнет.

// @ts-expect-error временный мост: контракт legacy-кода описан неверно
legacyCall();

Внешние библиотеки и типы

Для библиотек нужны типовые декларации. Часто они уже есть в пакете или в @types/....

Если типов нет, можно:

  • добавить минимальную декларацию вручную (.d.ts);
  • временно использовать any локально, но с планом замены.
declare module 'legacy-analytics' {
  export function track(event: string, payload?: unknown): void;
}

Проверь себя: почему лучше написать минимальный .d.ts, чем размазать any по всему проекту?

Границы между TS и JS

На границе модулей полезно делать явную нормализацию и валидацию входа.

type User = { id: number; name: string };

function toUser(raw: unknown): User | null {
  if (typeof raw === 'object' && raw !== null && 'id' in raw && 'name' in raw) {
    const r = raw as { id: unknown; name: unknown };
    if (typeof r.id === 'number' && typeof r.name === 'string') {
      return { id: r.id, name: r.name };
    }
  }
  return null;
}

Это защищает TS-код от неконтролируемых JS-данных.

Реальные микро-сценарии

  1. Постепенный переход фронтенда.

Новые компоненты пишутся на TS, старые JS-компоненты остаются, пока есть приоритет бизнеса.

  1. Node.js сервис.

Критичные модули (валидация, расчеты, контракты API) переводятся в TS первыми.

  1. Монорепа.

Общие пакеты типизируются раньше, чтобы дать безопасные контракты всем потребителям.

Частые ошибки новичков

  • Включать максимальную строгость сразу на весь legacy-код.
  • Массово ставить any без плана устранения.
  • Пытаться типизировать низкоприоритетные модули раньше критичных.
  • Игнорировать границы данных между JS и TS.

Анти-провал: миграцию планируй по ценности - сначала места с высоким риском багов и большим числом зависимостей.

Что будет, если изменить входные данные

Если JS-модуль начнет отдавать поле другого типа, строго типизированный TS-код подсветит несовместимость при интеграции. Без этого проблема всплыла бы позже в рантайме. Поэтому границы модулей - главная точка, где TypeScript дает максимальный эффект.

Проверь себя: какие 2-3 модуля в типичном проекте ты бы типизировал первыми и почему?

Краткий итог

  • Интеграция TS в JS-проект должна быть постепенной и управляемой.
  • checkJs и JSDoc помогают начать без полного переписывания.
  • На границах данных нужны явные проверки и нормализация.
  • Работа с типами внешних библиотек критична для надежных контрактов.
  • Успешная миграция - это баланс скорости разработки и роста типовой безопасности.