- Главная
- Typescript
- Миграция с JavaScript на TypeScript — первый шаг
Миграция с JavaScript на TypeScript — первый шаг
Содержание
- Стратегия миграции: постепенность — ключ к успеху
- Шаг 1: Переименование файлов
- Шаг 2: TSX — инструмент для быстрой итерации
- Шаг 3: Первые ошибки — и как их исправлять
- Шаг 4: Добавление аннотаций типов
- Шаг 5: Использование Type Guards и проверок
- Шаг 6: Type Aliases для переиспользования
- Шаг 7: Использование JSDoc для постепенной миграции
- Шаг 8: Постепенное ужесточение правил
- Практическое упражнение
- Итог
В предыдущих уроках мы изучили все основные возможности TypeScript: аннотации типов, массивы и объекты, Union Types, Type Guards и Aliases. Теперь настало время применить эти знания на практике. Но что делать, если у вас уже есть существующий JavaScript-проект? Переписывать всё с нуля — слишком радикально и дорого.
TypeScript создан с учётом этого сценария. Он позволяет мигрировать постепенно, файл за файлом, без остановки разработки. Сегодня мы разберём, как сделать первый шаг в этом процессе.
Стратегия миграции: постепенность — ключ к успеху
Миграция с JavaScript на TypeScript — это не спринт, а марафон. Вот основные принципы, которые я рекомендую:
- Начните с малого — переименуйте один файл с
.jsна.tsи посмотрите, что произойдёт. - Используйте
allowJs— разрешите TypeScript работать с вашими существующими.jsфайлами. - Включайте проверки постепенно — начните с минимальных настроек и ужесточайте их по мере продвижения.
- Не переписывайте всё сразу — сосредоточьтесь на добавлении типов в критически важные части кода.
Шаг 1: Переименование файлов
Самый простой способ начать миграцию — переименовать файл с расширением .js на .ts.
# Было
my-file.js
# Стало
my-file.ts
После этого TypeScript начнёт проверять этот файл. Если у вас есть зависимости от других .js файлов, они всё ещё могут работать, если вы включили опцию allowJs в конфигурации.
{
"compilerOptions": {
"allowJs": true, // Разрешаем импорт из .js файлов
"checkJs": false, // Пока не проверяем .js файлы
"noEmit": true // Не генерируем выходные файлы
}
}
Шаг 2: TSX — инструмент для быстрой итерации
В процессе миграции вам может понадобиться быстро запускать TypeScript-файлы без полной компиляции. Для этого существует инструмент TSX (TypeScript Execute).
# Установка TSX
npm install -g tsx
# Запуск TypeScript файла
tsx my-file.ts
TSX работает как node, но с поддержкой TypeScript на лету. Он не выполняет строгую проверку типов, но позволяет быстро тестировать код во время миграции.
Разница между TSX и TSC:
- TSX — выполняет файлы напрямую, без проверки типов. Хорош для быстрой разработки.
- TSC — компилирует TypeScript в JavaScript и проверяет типы. Используется для продакшн-сборок.
Шаг 3: Первые ошибки — и как их исправлять
Когда вы переименуете файл в .ts, вы увидите ошибки, которые раньше были скрыты. Это нормально. TypeScript нашёл проблемы, которые существовали в вашем коде, но вы их не замечали.
Пример: Файл до миграции (user.js)
// user.js
function getUser(id) {
return {
id: id,
name: "User " + id,
email: null // Иногда здесь null
};
}
function formatUser(user) {
return user.name.toUpperCase() + " (" + user.email + ")";
}
const user = getUser(1);
console.log(formatUser(user));
После переименования в user.ts:
// user.ts
function getUser(id) {
// ❌ Ошибка: Parameter 'id' implicitly has an 'any' type.
return {
id: id,
name: "User " + id,
email: null
};
}
function formatUser(user) {
// ❌ Ошибка: Parameter 'user' implicitly has an 'any' type.
return user.name.toUpperCase() + " (" + user.email + ")";
// ❌ Ошибка: 'user.email' может быть null
}
const user = getUser(1); // ❌ user имеет тип any
console.log(formatUser(user));
Оживи свой сайт. Освой JavaScript!
Статичная верстка — это только скелет. Наш онлайн-курс "JavaScript с нуля до профи" даст твоим страницам мышцы и нервы. Научись создавать слайдеры, формы, интерактивные карты и получать данные с сервера.
От теории — к реальным скриптам в твоём портфолио.
Шаг 4: Добавление аннотаций типов
Теперь мы начинаем добавлять типы, чтобы исправить ошибки. Начнём с самых очевидных мест:
// user.ts — первая итерация
function getUser(id: number): { id: number; name: string; email: string | null } {
return {
id: id,
name: "User " + id,
email: null
};
}
function formatUser(user: { id: number; name: string; email: string | null }): string {
// Но у нас всё ещё проблема с null
return user.name.toUpperCase() + " (" + user.email + ")";
// ❌ Ошибка: Object is possibly 'null'.
}
const user = getUser(1);
console.log(formatUser(user));
Шаг 5: Использование Type Guards и проверок
Чтобы исправить проблему с null, добавим проверку:
// user.ts — финальная версия
type User = {
id: number;
name: string;
email: string | null;
};
function getUser(id: number): User {
return {
id: id,
name: "User " + id,
email: null
};
}
function formatUser(user: User): string {
// Используем Type Guard для проверки на null
if (user.email === null) {
return `${user.name.toUpperCase()} (email not provided)`;
}
return `${user.name.toUpperCase()} (${user.email})`;
}
const user: User = getUser(1);
console.log(formatUser(user));
Теперь код работает без ошибок и полностью типизирован!
Шаг 6: Type Aliases для переиспользования
Обратите внимание, что мы использовали псевдоним типа User. Это хорошая практика — выносить повторяющиеся структуры в отдельные типы.
// types.ts — файл для общих типов
export type User = {
id: number;
name: string;
email: string | null;
};
export type Product = {
id: number;
name: string;
price: number;
inStock: boolean;
};
// user.ts — импортируем типы
import { User } from "./types";
function getUser(id: number): User {
// ...
}
Шаг 7: Использование JSDoc для постепенной миграции
Если вы не хотите полностью переходить на синтаксис TypeScript, вы можете использовать JSDoc-аннотации в .js файлах. TypeScript умеет читать их:
/**
* @param {number} id
* @returns {{ id: number; name: string; email: string | null }}
*/
function getUser(id) {
return {
id: id,
name: "User " + id,
email: null
};
}
/**
* @param {{ id: number; name: string; email: string | null }} user
* @returns {string}
*/
function formatUser(user) {
if (user.email === null) {
return user.name.toUpperCase() + " (email not provided)";
}
return user.name.toUpperCase() + " (" + user.email + ")";
}
Если вы включите checkJs: true в tsconfig.json, TypeScript будет проверять JSDoc-аннотации в .js файлах, давая вам преимущества типизации без изменения синтаксиса.
Шаг 8: Постепенное ужесточение правил
По мере продвижения миграции вы можете ужесточать настройки TypeScript:
{
"compilerOptions": {
"allowJs": true,
"checkJs": true, // Теперь проверяем .js файлы
"strict": true, // Включаем все строгие проверки
"noImplicitAny": true, // Запрещаем неявный any
"strictNullChecks": true, // Строгая проверка null/undefined
"target": "ES2020",
"module": "commonjs",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Практическое упражнение
Теперь ваша очередь попрактиковаться в миграции.
Задание: У вас есть JavaScript-файл shopping-cart.js. Ваша задача — мигрировать его на TypeScript, добавив все необходимые аннотации типов и исправив ошибки.
Исходный файл (shopping-cart.js):
// shopping-cart.js
function addToCart(cart, item) {
const existingItem = cart.find(i => i.id === item.id);
if (existingItem) {
existingItem.quantity += 1;
} else {
cart.push({ ...item, quantity: 1 });
}
return cart;
}
function calculateTotal(cart) {
let total = 0;
for (const item of cart) {
total += item.price * item.quantity;
}
return total;
}
function formatCart(cart) {
if (cart.length === 0) {
return "Cart is empty";
}
let result = "Shopping Cart:\n";
for (const item of cart) {
result += `- ${item.name}: $${item.price} x ${item.quantity} = $${item.price * item.quantity}\n`;
}
result += `Total: $${calculateTotal(cart)}`;
return result;
}
// Использование
let cart = [];
cart = addToCart(cart, { id: 1, name: "Laptop", price: 999.99 });
cart = addToCart(cart, { id: 2, name: "Mouse", price: 49.99 });
cart = addToCart(cart, { id: 1, name: "Laptop", price: 999.99 });
console.log(formatCart(cart));
Решение (не подглядывайте сразу!):
// shopping-cart.ts
type CartItem = {
id: number;
name: string;
price: number;
quantity: number;
};
type Cart = CartItem[];
function addToCart(cart: Cart, item: Omit<CartItem, "quantity">): Cart {
const existingItem = cart.find((i: CartItem) => i.id === item.id);
if (existingItem) {
existingItem.quantity += 1;
} else {
cart.push({ ...item, quantity: 1 });
}
return cart;
}
function calculateTotal(cart: Cart): number {
let total = 0;
for (const item of cart) {
total += item.price * item.quantity;
}
return total;
}
function formatCart(cart: Cart): string {
if (cart.length === 0) {
return "Cart is empty";
}
let result = "Shopping Cart:\n";
for (const item of cart) {
const itemTotal = item.price * item.quantity;
result += `- ${item.name}: $${item.price.toFixed(2)} x ${item.quantity} = $${itemTotal.toFixed(2)}\n`;
}
result += `Total: $${calculateTotal(cart).toFixed(2)}`;
return result;
}
// Использование
let cart: Cart = [];
cart = addToCart(cart, { id: 1, name: "Laptop", price: 999.99 });
cart = addToCart(cart, { id: 2, name: "Mouse", price: 49.99 });
cart = addToCart(cart, { id: 1, name: "Laptop", price: 999.99 });
console.log(formatCart(cart));
/* Вывод:
Shopping Cart:
- Laptop: $999.99 x 2 = $1999.98
- Mouse: $49.99 x 1 = $49.99
Total: $2049.97
*/
Что мы сделали:
- Создали типы
CartItemиCartдля описания структуры данных. - Добавили аннотации для всех параметров и возвращаемых значений.
- Использовали
Omit<CartItem, "quantity">для параметраitem, потому что при добавлении в корзину у товара ещё нет количества. - Добавили форматирование чисел с
toFixed(2)для корректного отображения цен.
Итог
Сегодня мы научились мигрировать JavaScript-код на TypeScript:
- Стратегия миграции — постепенность и работа с существующим кодом.
- Переименование файлов — первый шаг с
.jsна.ts. - TSX — инструмент для быстрого запуска TypeScript-файлов без компиляции.
- Исправление ошибок — добавление аннотаций типов и Type Guards.
- JSDoc — альтернативный способ добавления типов в
.jsфайлы. - Постепенное ужесточение — настройка
tsconfig.jsonпо мере продвижения миграции.
Миграция — это процесс, который требует терпения. Не пытайтесь сделать всё сразу. Начните с одного файла, добавьте типы, убедитесь, что всё работает, и двигайтесь дальше. TypeScript создан для постепенной адаптации, и это одно из его главных преимуществ.
В следующем уроке мы закрепим полученные знания на практике — вас ждёт упражнение по добавлению аннотаций типов в реальный код.