Node.js + TypeScript: настройка проекта с нуля в 2026 — tsx, paths, ESM
Настроить TypeScript-проект на Node.js в 2026 — задача на 15 минут, если знаешь правильные опции. Но выбор между ESM и CJS, tsx и ts-node, tsc и tsup может запутать. Разберём актуальный стек: ESM по умолчанию, tsx для разработки, tsup для сборки и flat config ESLint.
ESM vs CJS в 2026
В 2026 ESM — стандарт. Большинство новых пакетов публикуются только как ESM. Настройка:
{
"name": "my-service",
"type": "module",
"engines": { "node": ">=22" },
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsup",
"start": "node dist/index.js"
}
}
Ключевые моменты:
"type": "module"— все .js файлы трактуются как ESM. Для CJS используйте расширение .cjs.exports— точка входа для потребителей пакета. Заменяет устаревшее полеmain.- В ESM нет
require,__dirname,__filename. Замены:import.meta.urlиimport.meta.dirname(Node 22+).
tsx vs ts-node vs swc-node
Для запуска TypeScript без предварительной компиляции:
- tsx — обёртка над esbuild. Мгновенный старт (~50 мс), поддержка ESM и CJS, watch-режим из коробки. Рекомендация для 2026.
- ts-node — классика, но медленнее (~300 мс старт). Нужен для проектов с кастомными трансформерами.
- swc-node — быстрый, но менее стабильный с edge-case ESM-импортами.
# Установка tsx
npm i -D tsx
# Запуск
npx tsx src/index.ts
# Watch-режим с перезапуском
npx tsx watch src/index.ts
tsx не проверяет типы — только транспилирует. Проверку типов делает tsc --noEmit отдельно (в CI или pre-commit hook).
tsconfig.json: strict, paths, moduleResolution
{
"compilerOptions": {
"target": "ES2023",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"paths": {
"@/*": ["./src/*"],
"@config/*": ["./src/config/*"],
"@services/*": ["./src/services/*"]
},
"resolveJsonModule": true,
"isolatedModules": true,
"verbatimModuleSyntax": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Важные опции:
moduleResolution: "NodeNext"— правильное разрешение ESM-импортов (требует расширений в import).verbatimModuleSyntax— заменяет устаревшие importsNotUsedAsValues. Заставляет писатьimport typeявно.isolatedModules— совместимость с esbuild/swc (они компилируют файлы по одному).
Path aliases в рантайме
tsc понимает paths, но в скомпилированном JS пути остаются как @/services/db. Node.js их не разрешит. Решения:
1. tsup/esbuild — резолвят при сборке (рекомендуется):
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
// paths из tsconfig резолвятся автоматически
});
2. tsx — поддерживает paths из tsconfig без дополнительной настройки.
3. Node.js subpath imports (без внешних зависимостей):
{
"imports": {
"#config/*": "./src/config/*",
"#services/*": "./src/services/*"
}
}
Subpath imports (#-префикс) работают нативно в Node.js без транспиляции. Это самый надёжный вариант для production.
Сборка для прода: tsc vs tsup vs esbuild
- tsc — генерирует чистый JS 1:1 с исходниками. Медленный (~5 с на 500 файлов), но предсказуемый. Подходит для библиотек.
- tsup — обёртка над esbuild. Бандлит в один файл, tree-shaking, ~200 мс. Рекомендация для сервисов.
- esbuild напрямую — максимальный контроль, но больше конфигурации.
# tsup — сборка за 200 мс
npx tsup src/index.ts --format esm --dts
# tsc — классическая компиляция
npx tsc --project tsconfig.build.json
ESLint flat config + Prettier
В 2026 ESLint использует flat config (eslint.config.js). Старый .eslintrc deprecated:
// eslint.config.js
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.strictTypeChecked,
prettier,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
}],
},
},
{ ignores: ['dist/', 'node_modules/'] },
);
Итоговый стек для нового Node.js проекта в 2026: ESM + tsx (dev) + tsup (build) + strict tsconfig + flat ESLint. Настраивается за 10 минут, работает быстро, типы проверяются строго.