Node.js 20.9.0 LTS下如何用ts-node运行带ESM支持的TypeScript?
升级Node.js到20.9.0 LTS后TypeScript+Express项目运行错误解决
问题背景
将Node.js从18升级至20.9.0 LTS后,运行基于TypeScript+Express的项目时出现模块兼容错误,相关配置及报错信息如下:
项目配置
package.json
{ "name": "myproject", "version": "1.0.0", "private": "true", "license": "UNLICENSED", "main": "dist/index.js", "scripts": { "dev": "NODE_ENV=development nodemon", "dev-lite": "NODE_ENV=development node --loader ts-node/esm src/index.ts", }, "dependencies": {}, "devDependencies": {} }
tsconfig.json
{ "compilerOptions": { "module": "CommonJS", "esModuleInterop": true, "target": "ES6", "noImplicitAny": true, "moduleResolution": "node", "resolveJsonModule": true, "sourceMap": true, "outDir": "dist", "baseUrl": ".", "strict": true, "paths": { "*": ["node_modules/*"] } }, "include": ["src/**/*"] }
报错信息
- 初始运行
yarn dev-lite时报错:
yarn run v1.22.19 $ NODE_ENV=development node --loader ts-node/esm src/index.ts (node:12820) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' (Use `node --trace-warnings ...` to show where the warning was created) (node:12820) 警告:若要加载ES模块,请在package.json中设置"type": "module"或使用.mjs扩展名。 (node:12820) 警告:若要加载ES模块,请在package.json中设置"type": "module"或使用.mjs扩展名。 /home/mumin/welcome_localdev/sc-graphql-store-writer/src/index.ts:1 import app from './app'; ^^^^^^ SyntaxError: 无法在模块外部使用import语句 at internalCompileFunction (node:internal/vm:73:18) at wrapSafe (node:internal/modules/cjs/loader:1153:20) at Module._compile (node:internal/modules/cjs/loader:1205:27) at Module._extensions..js (node:internal/modules/cjs/loader:1295:10) at Module.load (node:internal/modules/cjs/loader:1091:32) at Module._load (node:internal/modules/cjs/loader:938:12) at cjsLoader (node:internal/modules/esm/translators:284:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:234:7) at ModuleJob.run (node:internal/modules/esm/module_job:217:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) Node.js v20.9.0 error Command failed with exit code 1.
- 添加
"type": "module"到package.json后新报错:
yarn run v1.22.19 $ NODE_ENV=development node --loader ts-node/esm src/index.ts (node:21831) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' (Use `node --trace-warnings ...` to show where the warning was created) file:///home/mumin/welcome_localdev/sc-graphql-store-writer/src/index.ts:5 Object.defineProperty(exports, "__esModule", { value: true }); ^ ReferenceError: ES模块作用域中未定义exports at file:///home/mumin/welcome_localdev/sc-graphql-store-writer/src/index.ts:5:23 at ModuleJob.run (node:internal/modules/esm/module_job:217:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) at async loadESM (node:internal/process/esm_loader:34:7) at async handleMainPromise (node:internal/modules/run_main:66:12) Node.js v20.9.0 error Command failed with exit code 1.
尝试将tsconfig.json的"module"改为"Node16"或"ESNext"后,仍出现相同的ReferenceError。
解决方案
1. 统一ES模块配置
核心是让TypeScript编译输出与Node.js的ES模块规则完全对齐:
修改package.json
- 保留
"type": "module",添加ts-node的ES模块专属配置 - 替换
--experimental-loader为Node.js 20推荐的--import参数
{ "name": "myproject", "version": "1.0.0", "private": "true", "license": "UNLICENSED", "main": "dist/index.js", "type": "module", "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" }, "scripts": { "dev": "NODE_ENV=development nodemon", "dev-lite": "NODE_ENV=development node --import ts-node/esm src/index.ts" }, "dependencies": {}, "devDependencies": {} }
修改tsconfig.json
调整编译选项适配ES模块:
{ "compilerOptions": { "module": "ESNext", "moduleResolution": "NodeNext", "target": "ES2020", "noImplicitAny": true, "resolveJsonModule": true, "sourceMap": true, "outDir": "dist", "baseUrl": ".", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"] }
module和moduleResolution设为ESNext/NodeNext,确保输出ES模块并遵循Node.js解析规则- 提升
target到ES2020,匹配Node.js 20的原生支持范围
2. 统一代码中的模块语法
检查项目所有文件,将CommonJS语法替换为ES模块语法:
- 替换
module.exports = xxx为export default xxx - 替换
exports.xxx = yyy为export const xxx = yyy
3. 更新依赖版本
确保ts-node和typescript为最新版本,避免旧版本的兼容性问题:
yarn add -D ts-node@latest typescript@latest
4. 适配nodemon(可选)
如果使用yarn dev命令,创建nodemon.json配置文件支持ES模块:
{ "watch": ["src"], "ext": "ts", "exec": "NODE_ENV=development node --import ts-node/esm src/index.ts" }
内容的提问来源于stack exchange,提问作者Muminur Rahman
相关产品推荐
相关产品推荐

