ts-node-dev для TypeScript: полное руководство с примерами
Добро пожаловать на PythonLib! В этой статье мы подробно разберем библиотеку ts-node-dev — незаменимый инструмент для современной TypeScript-разработки. Если вы пишете на TypeScript и устали вручную перекомпилировать код после каждого изменения, эта статья для вас.
1. Что это и зачем нужна
ts-node-dev — это утилита для TypeScript, которая обеспечивает горячую перезагрузку (hot reload) вашего приложения. Она перезапускает процесс Node.js автоматически при изменении исходных файлов TypeScript. Это значительно ускоряет разработку, так как вам не нужно вручную останавливать и запускать сервер или скрипт.
Зачем это нужно? В процессе разработки вы постоянно вносите изменения в код. Без автоматической перезагрузки каждый раз приходится:
- Останавливать текущий процесс (Ctrl+C)
- Запускать его заново (npm start или ts-node)
- Ждать компиляции и запуска
ts-node-dev решает эту проблему. Она отслеживает изменения в файлах проекта и мгновенно перезапускает приложение. В отличие от nodemon (который работает с JavaScript), ts-node-dev понимает TypeScript и использует ts-node под капотом для компиляции "на лету". Это делает её идеальным выбором для разработки серверов, CLI-инструментов и любых Node.js-приложений на TypeScript.
2. Установка
Установка ts-node-dev производится через npm (Node Package Manager). Рекомендуется устанавливать её как dev-зависимость, так как она нужна только в процессе разработки.
# Установка глобально (не рекомендуется)
npm install -g ts-node-dev
# Установка как dev-зависимость в проект (рекомендуется)
npm install --save-dev ts-node-dev
# Или через yarn
yarn add --dev ts-node-dev
# Или через pnpm
pnpm add -D ts-node-dev
После установки проверьте, что всё работает:
npx ts-node-dev --version
Если вы видите номер версии (например, 2.0.0), установка прошла успешно.
3. Быстрый старт — минимальный рабочий пример
Создадим простое TypeScript-приложение и запустим его с ts-node-dev.
Шаг 1: Инициализация проекта
mkdir my-ts-app
cd my-ts-app
npm init -y
npm install --save-dev typescript ts-node-dev @types/node
npx tsc --init
Шаг 2: Создаем файл src/index.ts
// src/index.ts
const greet = (name: string): string => {
return `Hello, ${name}!`;
};
console.log(greet('PythonLib'));
console.log('Server is running...');
// Экспортируем что-нибудь для примера
export { greet };
Шаг 3: Запускаем с ts-node-dev
npx ts-node-dev --respawn src/index.ts
Вы должны увидеть:
[INFO] 15:30:00 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.1, typescript ver. 5.3.3)
Hello, PythonLib!
Server is running...
Теперь измените текст приветствия в src/index.ts и сохраните файл. Вы увидите, что процесс автоматически перезапустится и выведет новый результат.
4. Основные методы/функции/классы
ts-node-dev — это CLI-инструмент, а не библиотека с API. Однако у него есть множество опций (флагов), которые можно рассматривать как "методы" настройки. Рассмотрим 8 ключевых опций.
4.1 --respawn
Сигнатура: ts-node-dev --respawn [файл]
Что делает: Автоматически перезапускает процесс при изменении любых файлов в проекте. Это основная опция для горячей перезагрузки.
Рабочий пример:
npx ts-node-dev --respawn src/index.ts
4.2 --transpile-only
Сигнатура: ts-node-dev --transpile-only [файл]
Что делает: Отключает проверку типов TypeScript при компиляции. Это значительно ускоряет перезапуск, но вы рискуете пропустить ошибки типов. Рекомендуется использовать в паре с отдельным процессом проверки типов (например, tsc --noEmit).
Рабочий пример:
npx ts-node-dev --transpile-only --respawn src/index.ts
4.3 --watch
Сигнатура: ts-node-dev --watch [путь,...] [файл]
Что делает: Указывает конкретные файлы или директории для отслеживания изменений. По умолчанию отслеживаются все файлы, но можно сузить круг для оптимизации.
Рабочий пример:
# Следить только за папками src и config
npx ts-node-dev --watch src,config --respawn src/index.ts
4.4 --ignore
Сигнатура: ts-node-dev --ignore [шаблон,...] [файл]
Что делает: Исключает файлы или директории из отслеживания. Полезно для игнорирования node_modules, логов, сгенерированных файлов.
Рабочий пример:
# Игнорировать node_modules и папку с логами
npx ts-node-dev --ignore node_modules,logs --respawn src/index.ts
4.5 --debounce
Сигнатура: ts-node-dev --debounce [мс] [файл]
Что делает: Устанавливает задержку (в миллисекундах) перед перезапуском после обнаружения изменения. Полезно, если вы сохраняете файл несколько раз подряд (например, автосохранение в IDE).
Рабочий пример:
# Ждать 1000 мс (1 секунду) перед перезапуском
npx ts-node-dev --debounce 1000 --respawn src/index.ts
4.6 --interval
Сигнатура: ts-node-dev --interval [мс] [файл]
Что делает: Устанавливает интервал опроса файловой системы (в миллисекундах). По умолчанию — 1000 мс. Меньшее значение увеличивает нагрузку на CPU, но делает перезагрузку более быстрой.
Рабочий пример:
# Проверять изменения каждые 500 мс
npx ts-node-dev --interval 500 --respawn src/index.ts
4.7 --prefer-ts-exts
Сигнатура: ts-node-dev --prefer-ts-exts [файл]
Что делает: Заставляет ts-node-dev отдавать предпочтение TypeScript-файлам (.ts, .tsx) перед JavaScript-файлами (.js) при разрешении импортов. Полезно в проектах, где есть и .ts, и .js файлы.
Рабочий пример:
npx ts-node-dev --prefer-ts-exts --respawn src/index.ts
4.8 --clear
Сигнатура: ts-node-dev --clear [файл]
Что делает: Очищает консоль перед каждым перезапуском. Это делает вывод более чистым и читаемым.
Рабочий пример:
npx ts-node-dev --clear --respawn src/index.ts
5. Реальный практический пример (100-200 строк кода)
Давайте создадим полноценный REST API сервер на Express с TypeScript и ts-node-dev для горячей перезагрузки.
Шаг 1: Установка зависимостей
npm install express
npm install --save-dev typescript ts-node-dev @types/express @types/node
Шаг 2: Создаем структуру проекта
mkdir -p src/routes src/models src/middleware
Шаг 3: Файл src/models/user.ts
// src/models/user.ts
export interface User {
id: number;
name: string;
email: string;
}
// Временное хранилище данных (в реальном проекте — база данных)
export const users: User[] = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
];
Шаг 4: Файл src/middleware/logger.ts
// src/middleware/logger.ts
import { Request, Response, NextFunction } from 'express';
export const requestLogger = (req: Request, res: Response, next: NextFunction): void => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.url}`);
next();
};
Шаг 5: Файл src/routes/userRoutes.ts
// src/routes/userRoutes.ts
import { Router, Request, Response } from 'express';
import { users, User } from '../models/user';
const router = Router();
// GET /api/users — получить всех пользователей
router.get('/users', (req: Request, res: Response) => {
res.json(users);
});
// GET /api/users/:id — получить пользователя по ID
router.get('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id, 10);
const user = users.find(u => u.id === id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
});
// POST /api/users — создать нового пользователя
router.post('/users', (req: Request, res: Response) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ message: 'Name and email are required' });
}
const newUser: User = {
id: users.length + 1,
name,
email,
};
users.push(newUser);
res.status(201).json(newUser);
});
// PUT /api/users/:id — обновить пользователя
router.put('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id, 10);
const userIndex = users.findIndex(u => u.id === id);
if (userIndex === -1) {
return res.status(404).json({ message: 'User not found' });
}
const { name, email } = req.body;
if (name) users[userIndex].name = name;
if (email) users[userIndex].email = email;
res.json(users[userIndex]);
});
// DELETE /api/users/:id — удалить пользователя
router.delete('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id, 10);
const userIndex = users.findIndex(u => u.id === id);
if (userIndex === -1) {
return res.status(404).json({ message: 'User not found' });
}
const deletedUser = users.splice(userIndex, 1)[0];
res.json({ message: 'User deleted', user: deletedUser });
});
export default router;
Шаг 6: Файл src/index.ts — точка входа
// src/index.ts
import express from 'express';
import userRoutes from './routes/userRoutes';
import { requestLogger } from './middleware/logger';
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json()); // для парсинга JSON тела запроса
app.use(requestLogger); // наш кастомный логгер
// Routes
app.use('/api', userRoutes);
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'OK', timestamp: new Date().toISOString() });
});
// Запуск сервера
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
console.log('Press Ctrl+C to stop');
});
export default app;
Шаг 7: Запуск с ts-node-dev
npx ts-node-dev --respawn --transpile-only --clear src/index.ts
Теперь откройте другой терминал и протестируйте API:
# Получить всех пользователей
curl http://localhost:3000/api/users
# Создать нового пользователя
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@example.com"}'
# Проверить health
curl http://localhost:3000/health
Попробуйте изменить любой файл в папке src — сервер автоматически перезагрузится с новыми изменениями!