Миграция JS в TS: стратегия
Миграция JavaScript-проекта в TypeScript — пошаговый процесс: от добавления tsconfig с мягкими настройками и переименования файлов до постепенного устранения
anyи включения строгого режима.
Зачем нужно
Единовременная полная типизация крупного JS-проекта нереалистична. Постепенная миграция позволяет добавить TypeScript в работающий продукт без остановки разработки, получая пользу сразу по мере продвижения.
Где используется
- Legacy frontend-проекты (React/Vue без TypeScript)
- Node.js backends, переходящие на TypeScript
- npm-библиотеки, добавляющие типы для пользователей
- Монорепозитории с постепенным переходом
Основной контент
Шаг 1: Начальная настройка (мягкий режим)
// tsconfig.json — минимально строгий для начала
{
"compilerOptions": {
"target": "ES2017",
"module": "CommonJS",
"allowJs": true, // разрешить .js файлы
"checkJs": false, // не проверять .js пока
"noImplicitAny": false, // разрешить implicit any
"strict": false, // отключить строгий режим
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Шаг 2: Переименование файлов
# Переименовать по одному файлу или всё сразу
for f in src/**/*.js; do mv "$f" "${f%.js}.ts"; done
# Или постепенно — начать с утилит и типов данных
mv src/utils/format.js src/utils/format.ts
mv src/models/user.js src/models/user.ts
Шаг 3: Добавление базовых типов
// Было (JS):
function getUser(id) {
return db.users.find(u => u.id === id);
}
// Стало (TS с any — первый шаг):
function getUser(id: string): any {
return db.users.find(u => u.id === id);
}
// Финал — после добавления интерфейса:
interface User {
id: string;
name: string;
email: string;
}
function getUser(id: string): User | undefined {
return db.users.find(u => u.id === id);
}
Шаг 4: Установка @types пакетов
# Основные типы для Node.js и популярных библиотек
npm install -D @types/node
npm install -D @types/express
npm install -D @types/lodash
npm install -D @types/jest
# Проверить доступность типов
npm info lodash types
# или ищи @types/имя-пакета на npmjs.com
Шаг 5: Постепенное ужесточение tsconfig
// Этап 1 → 2: включить checkJs
{ "checkJs": true }
// Этап 2 → 3: включить noImplicitAny
{ "noImplicitAny": true }
// Этап 3 → 4: включить strictNullChecks
{ "strictNullChecks": true }
// Финал: включить полный strict
{ "strict": true }
Паттерны замены any
// any → unknown + narrowing
function process(data: any) { // было
return data.name;
}
function process(data: unknown): string { // стало
if (typeof data === "object" && data !== null && "name" in data) {
return String((data as { name: unknown }).name);
}
return "";
}
// any → generic
function identity<T>(x: any): any { // было
return x;
}
function identity<T>(x: T): T { // стало
return x;
}
Работа с нетипизированными модулями
// Создать кастомные объявления типов
// src/types/legacy-module.d.ts
declare module "legacy-module" {
export function doSomething(x: string): number;
export const VERSION: string;
}
// Или быстрое решение (временно):
declare module "untyped-lib";
// всё из этого модуля будет any
Частые ошибки
- Мигрировать всё сразу — лучше один файл за раз, начиная с утилит и типов данных.
- Сразу включать
strict: true— слишком много ошибок убьёт мотивацию; начните с мягкого режима. - Игнорировать
@typesпакеты — без типов библиотек весь код взаимодействия с ними будетany. - Не добавлять
.d.tsдля legacy кода — для JS-файлов которые нельзя переписать, создайте отдельные объявления типов.
Связанные темы
- TypeScript -- установка и настройка
- tsconfig.json -- основные опции
- any, unknown -- различия
- Module Augmentation
- _MOC TypeScript