我正在检查某人的 .tsconfig
文件,并在那里发现了 --esModuleInterop
这是他的 .tsconfig
文件
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es6",
"module": "commonjs",
"lib": ["esnext"],
"strict": true,
"sourceMap": true,
"declaration": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"declarationDir": "./dist",
"outDir": "./dist",
"typeRoots": ["node_modules/@types"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modues"]
}
在这里,我的主要问题是什么是 "esModuleInterop": true,
和 "allowSyntheticDefaultImports": true,
。我知道他们有点依赖 "module": "commonjs",
。有人可以尝试用最好的人类语言来解释它吗?
allowSyntheticDefaultImports
个州的官方文档
允许从没有默认导出的模块进行默认导入。这不会影响代码发出,只是类型检查。
这意味着什么?如果没有任何导出默认值,那么我认为导入默认值的唯一用例是初始化某些东西?像单身?
以下问题/答案也没有意义Is there a way to use --esModuleInterop in tsconfig as opposed to it being a flag?
以及编译器页面上的 --esModuleInterop
定义
发出 __importStar 和 __importDefault 帮助器以实现运行时 babel 生态系统的兼容性,并启用 --allowSyntheticDefaultImports 以实现类型系统的兼容性。
我也似乎难以理解/理解
问题陈述
当我们想将 CommonJS 模块导入 ES6 模块代码库时会出现问题。
在这些标志之前,我们必须使用星号 (* as something
) 导入来导入 CommonJS 模块:
// node_modules/moment/index.js
exports = moment
// index.ts file in our app
import * as moment from 'moment'
moment(); // not compliant with es6 module spec
// transpiled js (simplified):
const moment = require("moment");
moment();
我们可以看到 *
在某种程度上等同于 exports
变量。它运行良好,但不符合 es6 模块规范。在规范中,星形导入中的命名空间记录(在我们的例子中是 moment
)只能是一个普通对象,不可调用(不允许 moment()
)。
解决方案
使用标志 esModuleInterop
,我们可以导入符合 es6
模块规范的 CommonJS 模块。现在我们的导入代码如下所示:
// index.ts file in our app
import moment from 'moment'
moment(); // compliant with es6 module spec
// transpiled js with esModuleInterop (simplified):
const moment = __importDefault(require('moment'));
moment.default();
它可以工作,并且与 es6 模块规范完全有效,因为 moment
不是来自星形导入的命名空间,它是默认导入。
但它是如何工作的?如您所见,因为我们执行了默认导入,所以我们调用了 moment
对象的 default
属性。但是我们没有在 moment 库中的 exports
对象上声明 default
属性。关键是 __importDefault
函数。它将模块 (exports
) 分配给 CommonJS 模块的 default
属性:
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
如您所见,我们按原样导入 es6 模块,但 CommonJS 模块被包装到具有 default
键的对象中。这使得在 CommonJS 模块上导入默认值成为可能。
__importStar
执行类似的工作 - 它返回未触及的 esModules,但将 CommonJS 模块转换为具有 default
属性的模块:
// index.ts file in our app
import * as moment from 'moment'
// transpiled js with esModuleInterop (simplified):
const moment = __importStar(require("moment"));
// note that "moment" is now uncallable - ts will report error!
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
合成进口
那么allowSyntheticDefaultImports
呢?它有什么用?现在文档应该很清楚了:
Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
在 moment
类型中,我们没有指定默认导出,我们也不应该指定,因为它只有在标志 esModuleInterop
开启时才可用。因此,如果我们要从没有默认导出的第三方模块导入默认值,allowSyntheticDefaultImports
将不会报告错误。
esModuleInterop
生成文档中概述的帮助程序。查看生成的代码,我们可以确切地看到它们的作用:
//ts
import React from 'react'
//js
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importDefault(require("react"));
__importDefault
:如果模块不是 es
模块,则 require 返回的内容成为默认值。这意味着如果您对 commonjs
模块使用默认导入,则整个模块实际上是默认模块。
__importStar
最好在 this PR 中描述:
TypeScript 将命名空间导入(即 import * as foo from "foo")等同于 const foo = require("foo")。这里的事情很简单,但是如果要导入的主要对象是原始对象还是带有调用/构造签名的值,它们就无法解决。 ECMAScript 基本上说命名空间记录是一个普通对象。 Babel 首先需要在模块中,并检查名为 __esModule 的属性。如果 __esModule 设置为 true,则行为与 TypeScript 相同,但除此之外,它会合成一个命名空间记录,其中: 所有属性都从需要的模块中提取出来,并作为命名导入提供。最初需要的模块作为默认导入提供。
所以我们得到这个:
// ts
import * as React from 'react'
// emitted js
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var React = __importStar(require("react"));
allowSyntheticDefaultImports
是所有这些的伴侣,将其设置为 false 不会更改发出的帮助器(它们看起来仍然相同)。但是,如果您对 commonjs 模块使用默认导入,它将引发打字稿错误。因此,如果 allowSyntheticDefaultImports
为 false
,此 import React from 'react'
将引发错误 Module '".../node_modules/@types/react/index"' has no default export.
。
现有答案的一个小补充 - 我在进行测试时遇到了这个问题。
为了修复它,我必须将 test
文件夹添加到 tsconfig
内的导入中:
{
...
"include": [
"src/**/*.ts",
"test/**/*.ts"
],
...
}
--esModuleInterop
标志之前,import * as moment from 'moment'
是不正确的。import moment = require('moment')
是。moment
是一个不好的例子。我不知道为什么我首先需要调用moment()
...