Короткий ответ: “адекватный транспайлинг” для Node.js зависит от того, как вы запускаете проект: CommonJS или ES modules. Для современного Node.js с ESM часто используют module: "NodeNext", moduleResolution: "NodeNext" и "type": "module" в package.json. Для старых или простых Node-проектов можно оставить module: "CommonJS"
Не существует одной настройки “ES6 для всех”. Важно согласовать три вещи: tsconfig.json, package.json и версию Node.js
Вариант 1: современный ESM-проект
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}
Пример src/index.ts:
import { readFile } from "node:fs/promises";
const text = await readFile("package.json", "utf8");
console.log(JSON.parse(text).type);
Сборка и запуск:
npm run build
npm run start
Вариант 2: CommonJS-проект
Если проект старый или использует require, настройка может быть такой:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"strict": true,
"rootDir": "src",
"outDir": "dist",
"esModuleInterop": true
},
"include": ["src"]
}
В package.json не ставьте "type": "module", если хотите CommonJS
Что значит target
target отвечает за версию JavaScript, в которую TypeScript будет преобразовывать синтаксис. Например, ES2022 оставит современные возможности, которые понимает свежий Node.js. Если у вас очень старый Node, target нужно снижать
Для Node.js не нужно бездумно ставить ES5: серверный runtime обычно современнее старых браузеров
Настройка для разработки через tsx
Во время разработки можно не собирать проект перед каждым запуском. Установите tsx:
npm install --save-dev tsx
Добавьте скрипты:
{
"scripts": {
"dev": "tsx src/index.ts",
"check": "tsc --noEmit",
"build": "tsc",
"start": "node dist/index.js"
}
}
Тогда рабочий процесс становится понятным:
npm run dev
npm run check
npm run build
npm run start
tsx удобен для разработки, но production-запуск лучше держать через собранный JavaScript из dist, если у проекта нет другой принятой схемы
Как понять, что выбрана не та схема
Если Node пишет ReferenceError: require is not defined in ES module scope, значит код запускается как ESM, но внутри остался CommonJS-подход
Если появляется Cannot use import statement outside a module, значит Node воспринимает итоговый файл как CommonJS, а внутри остался ESM-импорт
Эти ошибки почти всегда лечатся не случайными правками импортов, а приведением к одной схеме: либо CommonJS, либо ESM
Частые ошибки
Первая ошибка — поставить "type": "module", но оставить module: "CommonJS". Получается конфликт ожиданий
Вторая ошибка — использовать module: "ESNext" в Node-проекте и потом удивляться проблемам расширений и резолвинга. Для Node чаще понятнее NodeNext
Третья ошибка — импортировать локальный файл в ESM без расширения в итоговом JavaScript. При NodeNext приходится думать о том, как Node увидит готовые файлы
Четвертая ошибка — запускать src/index.ts напрямую через node. Node не компилирует TypeScript в обычном режиме. Сначала tsc, потом node dist/index.js, либо используйте tsx для разработки
Пятая ошибка — копировать browser-настройки в Node-проект. Для Node не нужны DOM-типы и не всегда нужен bundler-style resolution
Самопроверка
Выполните:
npx tsc --noEmit
npm run build
node dist/index.js
Если проверка типов проходит, сборка создает dist, а Node запускает готовый файл без ошибки модулей, транспайлинг настроен нормально
Что почитать дальше по TypeScript
Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:
- Node.js и TypeScript: как разбирать и исправлять ошибки
- TypeScript в Node.js: первый Express API
- React и TypeScript: как настроить и использовать в проекте
- Как разрабатывать и отлаживать Telegram-бота на Node.js, NestJS и TypeScript



