typescript 了解 tsconfig 文件中的 esModuleInterop
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/56238356/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understanding esModuleInterop in tsconfig file
提问by anny123
I was checking out someone .tsconfig
file and there I spotted --esModuleInterop
我正在检查某人的.tsconfig
文件,然后我发现--esModuleInterop
This is his .tsconfig
file
这是他的.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"]
}
Here, My primary question is what is "esModuleInterop": true,
and
"allowSyntheticDefaultImports": true,
. I know they are sort of dependent on the "module": "commonjs",
. Can someone try to explain it in the best human language possible?
在这里,我的主要问题是什么是 "esModuleInterop": true,
和
"allowSyntheticDefaultImports": true,
。我知道他们有点依赖 "module": "commonjs",
. 有人可以尝试用最好的人类语言来解释它吗?
The official docs for allowSyntheticDefaultImports
states
allowSyntheticDefaultImports
各州的官方文档
Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
允许从没有默认导出的模块中默认导入。这不会影响代码发出,只是类型检查。
Does that mean? if there isn't any export default than I think the only use case of the import default would be to initialise something? like singleton?
那是什么意思?如果没有任何导出默认值,我认为导入默认值的唯一用例是初始化某些东西?喜欢单身?
The following question/answer does not make sense as well Is there a way to use --esModuleInterop in tsconfig as opposed to it being a flag?
以下问题/答案也没有意义 有没有办法在 tsconfig 中使用 --esModuleInterop 而不是标志?
And --esModuleInterop
definition on the compiler page
和--esModuleInterop
编译器页面上的定义
Emit __importStar and __importDefault helpers for runtime babel ecosystem compatibility and enable --allowSyntheticDefaultImports for typesystem compatibility.
发出 __importStar 和 __importDefault 帮助程序以实现运行时 babel 生态系统兼容性,并启用 --allowSyntheticDefaultImports 以实现类型系统兼容性。
Also seemed difficult for me to understand/comprehend
我似乎也很难理解/理解
回答by Krzysztof Grzybek
Problem statement
问题陈述
Problem occurs when we want to import CommonJS module into ES6 module codebase.
当我们想要将 CommonJS 模块导入 ES6 模块代码库时会出现问题。
Before these flags we had to import CommonJS modules with star (* as something
) import:
在这些标志之前,我们必须使用 star ( * as something
) import导入 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();
We can see that *
was somehow equivalent to exports
variable. It worked fine, but it wasn't compliant with es6 modules spec. In spec, namespace record in star import (moment
in our case) can be only a plain object, not callable (moment()
is not allowed).
我们可以看到这*
在某种程度上等同于exports
变量。它运行良好,但不符合 es6 模块规范。在规范中,星形导入中的命名空间记录(moment
在我们的例子中)只能是一个普通对象,不可调用(moment()
不允许)。
Solution
解决方案
With flag esModuleInterop
we can import CommonJS modules in compliance with es6
modules spec. Now our import code looks like this:
使用 flagesModuleInterop
我们可以导入符合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();
It works and it's perfectly valid with es6 modules spec, because moment
is not namespace from star import, it's default import.
它可以工作并且完全适用于 es6 模块规范,因为moment
它不是来自星型导入的命名空间,而是默认导入。
But how it works? As You can see, because we did default import, we call default
property on moment
object. But we didn't declare any default
property on exports
object in moment library. The key is in __importDefault
function. It assigns module (exports
) to default
property for CommonJS modules:
但它是如何工作的?如您所见,因为我们进行了默认导入,所以我们default
在moment
对象上调用属性。但是我们没有在 moment 库中的 objectdefault
上声明任何属性exports
。关键在__importDefault
功能上。它将模块 ( exports
)分配给default
CommonJS 模块的属性:
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
As You can see, we import es6 modules as it is, but CommonJS modules are wrapped into object with default
key. This makes possible to import defaults on CommonJS modules.
如您所见,我们按原样导入了 es6 模块,但 CommonJS 模块使用default
key包装到对象中。这使得在 CommonJS 模块上导入默认值成为可能。
__importStar
does the similar job - it returns untouched esModules, but translate CommonJS modules into modules with default
property:
__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;
};
Synthetic imports
合成进口
And what about allowSyntheticDefaultImports
, what it is for? Now the docs should be clear:
那么allowSyntheticDefaultImports
,它的用途是什么?现在文档应该很清楚了:
Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
In moment
typings we don't have specified default export, and we shouldn't have, beacuse it's available only with flag esModuleInterop
on. So allowSyntheticDefaultImports
just won't report error if we want to import default from thrid party module which don't have default export.
在moment
打字中,我们没有指定默认导出,我们也不应该有,因为它只有在esModuleInterop
打开标志时才可用。所以allowSyntheticDefaultImports
如果我们想从没有默认导出的第三方模块导入默认值,就不会报错。
回答by Titian Cernicova-Dragomir
esModuleInterop
generates the helpers outlined in the docs. Looking at the generated code, we can see exactly what these do:
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
: If the module is not an es
module then what is returned by require becomes the default. This means that if you use default import on a commonjs
module, the whole module is actually the default.
__importDefault
: 如果模块不是es
模块,那么 require 返回的内容将成为默认值。这意味着如果你在一个commonjs
模块上使用默认导入,整个模块实际上是默认的。
__importStar
is best described in this PR:
__importStar
在这个 PR 中得到了最好的描述:
TypeScript treats a namespace import (i.e.
import * as foo from "foo"
) as equivalent toconst foo = require("foo")
. Things are simple here, but they don't work out if the primary object being imported is a primitive or a value with call/construct signatures. ECMAScript basically says a namespace record is a plain object.Babel first requires in the module, and checks for a property named
__esModule
. If__esModule
is set totrue
, then the behavior is the same as that of TypeScript, but otherwise, it synthesizes a namespace record where:
- All properties are plucked off of the require'd module and made available as named imports.
- The originally require'd module is made available as a default import.
TypeScript 将命名空间导入(即
import * as foo from "foo"
)视为等同于const foo = require("foo")
. 事情在这里很简单,但是如果要导入的主要对象是原始对象或具有调用/构造签名的值,则它们不起作用。ECMAScript 基本上说命名空间记录是一个普通对象。Babel 首先需要在模块中,并检查名为
__esModule
. 如果__esModule
设置为true
,则行为与 TypeScript 相同,但除此之外,它会合成一个命名空间记录,其中:
- 所有属性都从 require'd 模块中提取出来,并作为命名导入提供。
- 最初需要的模块作为默认导入提供。
So we get this:
所以我们得到这个:
// 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
is the companion to all of this, setting this to false will not change the emitted helpers (both of them will still look the same). But it will raise a typescript error if you are using default import for a commonjs module. So this import React from 'react'
will raise the error Module '".../node_modules/@types/react/index"' has no default export.
if allowSyntheticDefaultImports
is false
.
allowSyntheticDefaultImports
是所有这些的伴随,将其设置为 false 不会改变发出的助手(它们看起来仍然相同)。但是,如果您对 commonjs 模块使用默认导入,则会引发打字稿错误。所以如果是,这import React from 'react'
将引发错误。Module '".../node_modules/@types/react/index"' has no default export.
allowSyntheticDefaultImports
false