我正在构建一个discord.js
Discord机器人。由于某种原因,discord.js
不能与ESM
模块一起工作(一个完全独立的问题),所以我的机器人应用程序使用CommonJS
模块。现在我的系统上有另一个名为Lib
的项目,它有很多实用函数,我计划在几个不同的项目中使用,所以我不必重写它们。这个Lib
项目使用ESM
模块。由于我我必须从DiscordBot
导入Lib
,我使用typescript中的动态导入语法。现在,每当我转译我的DiscordBot
项目时,动态导入都会转换为 * 一些丑陋的JavaScript模块代码 *,而这些丑陋的模块代码最终使用require()。由于require()无法导入ESM模块,我的bot最终崩溃。
然而,我试图停止我的ts编译器,从导入Lib
的ts文件中复制代码,然后手动将该代码粘贴到相应的JS文件中(并删除TS专有的功能,如类型注解和接口)。然后我运行我的bot应用程序,但是我不想每次都这样做,所以问题出在tsc
的编译上,我该怎么解决呢?
8条答案
按热度按时间hgb9j2n61#
所以我理解的目的是:
1.使用TypeScript开发代码
1.运行CommonJS包中的编译代码
1.导入并使用ES模块
选项一:
如果
tsconfig.json
中的"module"
被设置为"commonjs"
,目前没有办法阻止TypeScript将动态import()
转换为require()
-除了将代码隐藏在字符串中并使用eval
执行它。像这样:字符串
TypeScript不可能转译字符串!
选项二
将
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语法导入(“模块”设置为“es 2020”),无法在CommonJS包中运行。下面是一点信息:
"module"
设置为"es2020"
:动态导入import()
不会被转译。"module"
被设置为“es 2015”:则会出现错误:TS 1323:仅当“--module”标志设置为“es 2020”、“esnext”、“commonjs”、“amd”、“system”或“umd”时,才支持动态导入。
"module"
被设置为"commonjs"
:动态导入被转译。引用
"module"
字段的tsconfig.json
参考:如果你想知道ES 2015和ES 2020之间的区别,ES 2020增加了对动态导入和import. meta的支持。
svmlkihl2#
其他人谈论的
node12
设置对我不起作用,但这些compilerOptions
可以,使用Typescript 4.7.2:字符串
这节省了我的背部,我不必将所有
import require
迁移到导入,以便能够使用ESM npm库。Typescript输入源:
型
CommonJS输出:
型
8nuwlpux3#
这目前是不可能的。GitHub(https://github.com/microsoft/TypeScript/issues/43329)有一个非常新的问题,但尚未实现。所以你现在所能做的就是用你的
Lib
项目从ESM切换到CommonJS。更新2022
这个问题已经解决,现在有一个新的
"module"
选项,名为node12
。这应该可以解决这个问题。6ojccjat4#
我正在使用已经提到的基于
eval
的黑客的一个变体来克服这个问题。例如,
parse-domain
是作为ESM模块分发的,因此像这样导入它会破坏基于CJS的节点应用程序:字符串
这就是我如何让它工作的:
型
(Note
parseDomainFromUrl
在进程中变得异步,因此调用者需要等待它。8wigbo565#
添加我的答案,因为这显然仍然是2023年的一个问题,没有解决方案,保存eval hack是万无一失的。使用commonjs以外的任何东西作为模块设置可能会破坏其他模块的编译。在我们的例子中,firebase模块的负载在设置commonjs以外的任何东西时都有问题。
具体来说,我们需要阻止typescript将动态导入转换为require
对我们有效的选项是将
module
选项设置为"NodeNext"
,然后将skipLibCheck
设置为true
,以抑制由我们导入的依赖项之一引起的错误。这不像我们真的可以用该依赖项更改任何内容(因为它被googlecloud模块使用)。根据你的观点,这并不理想,但js模块系统也不理想。下面粘贴我们的
tsconfig.json
配置:字符串
qnzebej06#
这个问题已通过为
module
设置添加node12
选项得到修复。从文档中:node 12和nodenext模式在夜间版本中可用,实验性的node 12和nodenext模式与Node的原生ECMAScript Module支持集成。发出的JavaScript使用CommonJS或ES 2020输出,具体取决于文件扩展名和最近的package.json中的类型设置值。模块解析也有不同的工作方式。您可以在手册中了解更多信息。
但是,如果您使用此设置而不进行夜间构建,则当前会产生以下错误:
错误TS 4124:值为“node 12”的编译器选项“module”不稳定。请使用夜间TypeScript消除此错误。请尝试使用“npm install -D typescript@ next”进行更新。
kcwpcxri7#
我参加聚会要迟到了,但我也想在这里留下我的解决方案。使用
js eval
(不需要包,这是一个原生的js函数)我创建了一个approxc类型的esm模块导入器:字符串
对我来说,这就像一个魅力:)
如果你愿意,我还创建了this npm包。
希望这对某人有帮助!
q7solyqu8#
你用的是什么编译器/打包器?我假设tsc是基于上下文的。
我推荐使用esbuild来编译和捆绑你的TS。你也可以在使用tsc之后使用它来简单地转换它。它有一个名为“format”的选项,可以删除任何模块样式的导入。请访问https://esbuild.github.io/api/#format。
以下是使用的简单范例。
build.js
字符串
然后,您可以向package.json中添加如下内容
型
这里有一个附加的链接,是关于使用带有typescript的esbuild的一些注意事项。这些对您来说都不是问题。https://esbuild.github.io/content-types/#typescript-caveats