在typescript编译器选项#module中,我们有一系列选项,包括nodenext
和esnext
,其中nodenext
是实验性的(截至目前).
为什么我们需要这个额外的nodenext
选项,而esnext
选项似乎已经在使用node.js?
或者换言之,nodenext
和esnext
之间有什么区别?
在typescript编译器选项#module中,我们有一系列选项,包括nodenext
和esnext
,其中nodenext
是实验性的(截至目前).
为什么我们需要这个额外的nodenext
选项,而esnext
选项似乎已经在使用node.js?
或者换言之,nodenext
和esnext
之间有什么区别?
module
and moduleResolution
首先需要澄清的是module
和moduleResolution
编译器选项的区别.前者是一个emit设置:tsc愿意向JS发送哪些与模块相关的代码?查看此选项效果的最简单方法是在设置commonjs
和esnext
之间切换:
Input code | Output --module commonjs |
Output --module esnext |
---|---|---|
import { createSourceFile } from "typescript" |
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const typescript_1 = require("typescript"); |
import { createSourceFile } from "typescript" |
虽然此设置基本上控制emits ,但它可以对允许的模块相关输入代码施加限制.例如,您不能以--module es2015
以下import fs = require("fs")
(或更高ES目标)的样式写入导入,因为ES模块系统中没有require
.此外,仅在--module es2022
(或更高)或system
中允许使用顶级await
,因为它需要模块加载系统中的相应支持.
另一方面,--moduleResolution
是用来回答以下问题的算法:"给定一个文件系统和一些包含"lodash"
导入的输入文件,我应该寻找哪些文件来找到该模块?"显然,查找具有神奇名称node_modules
的文件夹的决定与Node有关(尽管为了方便起见,大量非Node工具已经复制了该文件夹),并且不会对每个可能的运行时都是正确的.
moduleResolution
在这样的背景下,我们准备开始直接回答您的问题.--module nodenext
和--module esnext
之间最大、最明显的区别是前者意味着--moduleResolution nodenext
,这是一种新的解析模式,专为 node 具体实现共存的ESM和CJ而设计,而后者does not imply a 104 setting because there is no such corresponding setting in TypeScript right now.换句话说,当你说你使用--module esnext
时,你可以写,我们将发出,最新和最好的ES模块代码构造,但我们在决定导入如何解析时不会做任何不同的事情.您可能会继续使用--moduleResolution node
,它是为Node实现CJS而设计的.这对你意味着什么?如果你正在为Node编写ESM,你可能会让一些东西在--module esnext
和--moduleResolution node
上工作,但是像package.json exports这样更新的特定于Node的功能将不起作用,而且在编写导入路径时,你很容易就被击中了脚.tsc将根据 node 的CJS规则判断路径,但在运行时, node 将根据其ESM规则判断路径,因为您正在emits ESM.这些算法之间有显著的差异,尤其是后者需要相对导入来使用文件扩展名,而不是删除.js
,而index
文件没有特殊意义,因此不能仅通过命名目录路径来导入索引文件.
module
在--module
设置中观察到的差异本身就有点微妙.正如我之前提到的,在esnext
中,你不能写import Foo = require("bar")
,因为我们假设没有require
.在nodenext
中,我们知道给定模块可能是ES模块or,也可能是CJS模块,基于其文件扩展名(.mts
)→ .mjs
意味着ESM和.cts
→ .cjs
表示CJS)和/或最近包装中的type
字段.json文件.--module nodenext
支持查看这些内容,从而决定给定文件的模块类型,控制我们发出的模块输出类型.如果上述条件导致模块被解释为CJS,则该文件的输出几乎相同(可能相同?)你能从--module commonjs
得到什么.如果模块被解释为ESM,那么输出与您从--module esnext
获得的结果非常相似,但有一个例外,我可以从头回忆起:您仍然可以写入import Foo = require("bar")
,并且编译为:
import { createRequire as _createRequire } from "module";
const __require = _createRequire(import.meta.url);
const Foo = __require("bar");
我认为你问题的答案可以总结如下:
type
和特殊的文件扩展名,所以我们需要一个理解这些内容的模块emits 模式.该模式大致可以被认为是现有commonjs
和esnext
模式之间的基于 node 的 Select 器,并根据 node 定制了一些额外的小差异.