【问题标题】:How to prevent typescript from transpiling dynamic imports into require()?如何防止打字稿将动态导入转换为require()?
【发布时间】:2021-03-23 16:47:30
【问题描述】:

我正在构建一个discord.js Discord 机器人。现在由于某种原因,discord.js 不适用于ESM 模块(一个完全独立的问题),所以我的机器人应用程序使用CommonJS 模块。现在我的系统上有另一个名为Lib 的项目,它有很多实用功能,我计划在几个不同的项目中使用,所以我不必重写它们。这个Lib 项目使用ESM 模块。因为我必须从DiscordBot 导入Lib,所以我在打字稿中使用动态导入语法。现在,每当我转换我的DiscordBot 项目时,动态导入都会被转换成一些丑陋的javascript 模块代码,而那个丑陋的模块代码最终会使用require()。由于 require() 无法导入 ESM 模块,我的机器人最终崩溃了。

然而,我尝试停止我的 ts 编译器,从我的 ts 文件中复制导入 Lib 的代码,然后手动将该代码粘贴到相应的 JS 文件中(并删除 TS 独有的功能,如类型注释和接口)。然后我运行了我的机器人应用程序,它运行得非常好。但我不想每次都这样做。所以这是tsc 的编译问题。我该如何解决这个问题?

【问题讨论】:

  • 你探索过module option吗?
  • 嗯,当然可以。这就是我将 Lib 设置为 ESM 模块并将 DiscordBot 设置为 CommonJS 模块的方式。
  • 你能在这里分享你的tsconfig.json吗?

标签: javascript node.js typescript tsc dynamic-import


【解决方案1】:

所以我理解的目的是:

  1. 在 TypeScript 中开发代码
  2. 运行CommonJS包中编译好的代码
  3. 导入和使用 ES 模块

选项 1:

如果tsconfig.json 中的"module" 设置为"commonjs",目前无法阻止TypeScript 将动态import() 转换为require() - 除非您将代码隐藏在字符串中并使用eval执行它。像这样:

async function body (pMap:any){
   // do something with module pMap here
}

eval ("import('p-map').then(body)");

TypeScript 无法转换字符串!


选项 2

tsconfig.json 中的"module" 设置为"es2020"。通过这样做,动态导入不会被转译为require(),您可以使用动态导入来导入 CommonJS 或 ES 模块。或者,您可以使用 const someModule = require("someModule") 语法来导入 CommonJS 模块(不会被转译为 ES6 导入语法)。您不能使用 ES6 导入语法,例如 import * as someModule from "someModule"import someModule from "someModule"。这些语法会发出 ES Module 语法导入(“module”设置为“es2020”),并且不能在 CommonJS 包中运行。


以下是一些信息:

  • 如果"module" 设置为"es2020":动态导入import() 不会被转译。

  • 如果"module" 设置为`"es2015": 有错误:

    TS1323:仅当“--module”标志设置为“es2020”、“esnext”、“commonjs”、“amd”、“system”或“umd”时才支持动态导入。

  • 如果 "module" 设置为 "commonjs":动态导入被转译。

引用tsconfig.json 引用"module" 字段:

如果您想知道 ES2015 和 ES2020 之间的区别, ES2020 增加了对动态导入和 import.meta 的支持。

【讨论】:

  • 我向 OP 索要了一个tsconfig.json,但我现在意识到这个问题已经存在了多久,而且它只出现在我身上,因为你给了它一个赏金。你能分享tsconfig.json吗?更好的是最小的测试用例。也许是粘贴,甚至是一个新问题。
  • 我的回答与tsconfig.json无关。如果 tsconfig.json 中的设置可以阻止 typescript 将动态导入传递到 require(),那将是一个美化的答案。我的回答很丑 - 它在字符串中隐藏了动态导入语法,并使用 'eval' 来执行它。打字稿不会传递字符串。
【解决方案2】:

您使用的是什么编译器/捆绑器?我假设 tsc 基于上下文。

我建议使用 esbuild 来编译和捆绑你的 TS。您也可以在使用 tsc 后简单地使用它来转换它。它有一个名为“格式”的选项,可以删除任何模块样式的导入。见https://esbuild.github.io/api/#format

这是一个简单的使用示例。

build.js

const esbuild = require("esbuild");

esbuild.build({
   allowOverwrite: true,
   write: true,
   entryPoints: ["my-main-file.ts"],
   outfile: "some-file.bundle.js",
   format: "cjs", //format option set to cjs makes all imports common-js style
   bundle: true,
}).then(() => {
   console.log("Done!");
});

然后您可以在 package.json 中添加类似的内容

"scripts": {
        "build": "node build.js",
...rest of scripts

这里是关于使用 esbuild 和 typescript 的一些注意事项的附加链接。这些对你来说都不是问题。 https://esbuild.github.io/content-types/#typescript-caveats

【讨论】:

    【解决方案3】:

    目前这是不可能的。 GitHub 上有一个非常新的问题 (https://github.com/microsoft/TypeScript/issues/43329),但尚未实现。因此,您现在可以做的就是使用 Lib 项目从 ESM 切换到 CommonJS。

    【讨论】:

    • 有可能。请看下面我的回答。这很丑陋,但它有效。
    猜你喜欢
    • 2023-01-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 2015-07-23
    • 2020-12-03
    • 2023-02-18
    • 2021-11-09
    相关资源
    最近更新 更多