如何使用 NodeJS 13 和 Typescript 3.8 导入 esm 模块?

Posted

技术标签:

【中文标题】如何使用 NodeJS 13 和 Typescript 3.8 导入 esm 模块?【英文标题】:How to import esm modules with NodeJS 13, and Typescript 3.8? 【发布时间】:2020-08-13 20:36:10 【问题描述】:

我在 NodeJS 中的一些导入有问题。我想使用 Typescript 3.8 的新功能,比如私有字段:#myPrivateField

我不知道如何在课堂上正确导入模块“typescript”。我尝试了很多选择,但无法解决我的问题。

我的文件:

package.json


  "name": "test",
  "scripts": 
    "start": "tsc && node --experimental-modules --es-module-specifier-resolution=node main.js"
  ,
  "dependencies": 
    "@types/node": "^13.13.2",
    "app-root-path": "^3.0.0",
    "fs-extra": "^9.0.0",
    "tsutils": "^3.17.1"
  ,
  "devDependencies": 
    "ts-node": "~8.3.0",
    "typescript": "^3.8.3"
  ,
  "type": "module"

tsconfig.json


  "compilerOptions": 
    "lib": [
      "ESNext",
      "es2016",
      "dom",
      "es5"
    ],
    "module": "esnext",
    "moduleResolution": "Node",
    "sourceMap": true,
    "target": "es6",
    "typeRoots": [
      "node_modules/@types"
    ]
  

ma​​in.ts

// import ts = require("typescript");
import * as ts from "typescript";

export class Main 

    node: ts.Node;
    #test = 'zzz';

    constructor() 

    process(): void 
        ts.forEachChild(this.node, function cb() 
        );
        console.log('#test', this.#test);
    


const main = new Main();
main.process();

使用此代码,当我运行 npm run start 时,出现错误 TypeError: ts.forEachChild is not a function

如果没有ts.forEachClid() 行,它会正确记录私有字段#test 的值。

如果我尝试将import * as ts from "typescript"; 替换为import ts = require("typescript");,则会出现错误TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import a from "mod"', 'import d from "mod"', or another module format instead

当然,我在 tsconfig.json 和 package.json 中尝试了很多更改(使用 `"type" = "module"),但无法解决这个问题。

我什至尝试将"module": "esnext" 替换为"module": "commonjs",但出现错误exports is not defined

备注: 这不是特定于模块“打字稿”。我对“fs-extra”等其他模块也有同样的问题,它们以不同于大多数经典 NodeJS 模块的方式进行导出。

例如,模块“typescript”导出为export = ts

我也找到了这个参考,但它对我没有多大帮助: https://www.typescriptlang.org/docs/handbook/modules.html

我的 nodeJs 版本是 13.3.0,我的 typescript 版本是 3.8.3 感谢您的帮助

【问题讨论】:

我不知道typescript,但我认为您的问题与导入无关。如您所见ts.Node 已解决,然后您正确导入typescript。根据TypeScript doc,你应该像这样定义私有字段:#test: string;并在构造函数中初始化它:constructor() this.#test = 'zzz'; 您好,感谢您的回复。但是不,在 Typescript 中,您可以在构造函数之外声明和初始化属性。 Typescript 只会通过在 javascript 构造函数中设置它们的值来将它们转换为 javascript。 【参考方案1】:

最后,好的回应是:您需要commonjses2018 才能在节点模块中使用打字稿#privateFields。

这是要使用的正确 tsconfig.json:


    "compileOnSave": false,
    "compilerOptions": 
        "module": "commonjs",
        "moduleResolution": "node",
        "target": "es2018",
        "typeRoots": [
            "node_modules/@types"
        ],
        "lib": [
            "es2018",
            "dom"
        ]
    

【讨论】:

【参考方案2】:

ESM 模块仍处于实验模式,有很多模块不支持它,例如使用 module.exports (CommonJS) 而不是 export (ESM)。

您最好的选择是使用commonjs 模块并运行node 而不带任何标志。另外,package.json 中的包的type 也必须是commonjs


回答您的问题“如何导入 ESM 模块”...如果模块支持 ESM 模块,您可以像往常一样使用import

import  something  from './something';

UPD:正如 OP 作者所说,要使私有字段起作用,必须设置一个目标 es2018。这背后的原因是私有字段不是 ES2015 规范的一部分,您需要升级到最低支持目标才能做到这一点。

"compilerOptions": 
  "module": "commonjs",
  "moduleResolution": "node",
  "target": "es2018"

【讨论】:

您好,感谢您的回复!没错,最好的选择是使用commonjs,但您也需要使用es2018 以避免其他错误。我在下面的帖子中写了正确的答案。感谢您的帮助! 我已经更新了答案,以解释您需要升级目标的原因。如果一切都为您解决,请随时接受它作为您问题的答案。谢谢!

以上是关于如何使用 NodeJS 13 和 Typescript 3.8 导入 esm 模块?的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript基础教程

TypeScript基础教程

TypeScript基础教程

如何在 CentOS7 中安装 Nodejs

typescript入门第一课

Nodejs,expressjs - 如何提供延迟响应