Avatar ·

Ошибка TypeScript: ReferenceError: exports is not defined при сборке

Привет всем! Я новичок в TypeScript и столкнулся с проблемой при попытке скомпилировать и запустить свой проект. Использую Node.js (версия 18) и TypeScript (версия 5.0). Вот мой простой код: ```typescript // math.ts export function add(a: number, b: number): number { return a + b; } export const PI = 3.14; ``` ```typescript // main.ts import { add, PI } from './math'; console.log(add(2, 3)); console.log(PI); ``` Я скомпилировал код командой `tsc main.ts` и получил файлы `math.js` и `main.js`. Когда я запускаю `node main.js`, возникает ошибка: ``` ReferenceError: exports is not defined at Object. (/path/to/main.js:2:23) ``` При этом в сгенерированных JS-файлах я вижу использование `exports.add = ...` и `exports.PI = ...`. Я читал, что это как-то связано с модулями, но не понимаю, что именно я делаю не так. Мой `tsconfig.json` выглядит так: ```json { "compilerOptions": { "target": "ES6", "module": "commonjs", "outDir": "./dist", "rootDir": "./src" } } ``` Подскажите, пожалуйста, как исправить эту ошибку? Я хочу, чтобы код работал в Node.js без дополнительных инструментов вроде Webpack. <<>>

Причина ошибки

Ошибка ReferenceError: exports is not defined возникает из-за несоответствия между системой модулей, которую генерирует TypeScript, и тем, как Node.js обрабатывает эти модули в вашем окружении. Вы используете "module": "commonjs", что заставляет TypeScript генерировать код с exports и require. Однако Node.js может не распознавать exports как глобальную переменную, если вы запускаете файлы напрямую без правильной настройки.

По умолчанию Node.js работает с CommonJS (CJS), где exports — это псевдоним для module.exports. Но если ваш проект настроен как ES-модуль (например, в package.json указано "type": "module"), то exports не определён, и возникает ошибка. В вашем случае, судя по всему, Node.js ожидает ES-модули, а TypeScript генерирует CommonJS.

Решение 1: Использовать CommonJS (рекомендуется для Node.js)

Если вы хотите использовать CommonJS (стандарт для Node.js), убедитесь, что ваш package.json не содержит "type": "module". Если файл package.json есть, удалите эту строку или измените на "type": "commonjs". Затем скомпилируйте TypeScript как обычно:

tsc
node dist/main.js

Убедитесь, что в tsconfig.json указано "module": "commonjs" (как у вас). После компиляции должен получиться рабочий код.

Решение 2: Использовать ES-модули (современный подход)

Если вы предпочитаете ES-модули (import/export), измените настройки TypeScript и Node.js:

  1. В package.json добавьте "type": "module".
  2. В tsconfig.json измените "module": "ESNext" или "module": "ES2020".
  3. Скомпилируйте и запустите: tsc && node dist/main.js.

Пример обновлённого tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "moduleResolution": "node"
  }
}

Обратите внимание: при использовании ES-модулей в Node.js файлы должны иметь расширение .mjs или быть в папке с "type": "module". TypeScript автоматически сгенерирует .js файлы, но Node.js будет воспринимать их как ES-модули благодаря настройке в package.json.

Решение 3: Использовать ts-node для разработки

Если вы не хотите каждый раз компилировать код, установите ts-node и tslib:

npm install -D ts-node tslib @types/node

Затем запускайте напрямую:

npx ts-node src/main.ts

Этот инструмент автоматически обрабатывает модули и не требует настройки package.json.

Проверка окружения

Чтобы убедиться, что проблема именно в модулях, выполните команду node -e "console.log(typeof exports)". Если вернёт undefined, значит Node.js работает в режиме ES-модулей. Если вернёт object — CommonJS. Это поможет выбрать правильное решение.

Рекомендую начать с Решения 1 (CommonJS), так как оно наиболее стабильно для серверных приложений на Node.js. Если вы изучаете современный стандарт, переходите к Решению 2 (ES-модули).

<<>>

Альтернативный подход: Использовать модуль "NodeNext"

В TypeScript 4.7+ появилась поддержка нового режима модулей "NodeNext", который автоматически определяет, какую систему модулей использовать на основе package.json. Это избавляет от ручной настройки.

Обновите tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

Теперь TypeScript будет следовать правилам Node.js: если в package.json есть "type": "module", он сгенерирует ES-модули; если нет — CommonJS. Это делает конфигурацию более гибкой и меньше подверженной ошибкам.

Важно: при использовании "module": "NodeNext" в импортах нужно указывать расширение файла (например, import { add } from './math.js'), даже в TypeScript-коде. Это соответствует поведению Node.js. Пример:

// main.ts
import { add, PI } from './math.js';

console.log(add(2, 3));
console.log(PI);

Этот подход особенно полезен, если вы планируете публиковать пакет в npm, так как он соответствует современным стандартам и работает как с CommonJS, так и с ES-модулями.