了解tsconfig文件中的esModuleInterop

ann*_*123 31 typescript

我正在检查某人的.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"]
}
Run Code Online (Sandbox Code Playgroud)

在这里,我的主要问题是 "esModuleInterop": true,"allowSyntheticDefaultImports": true,。我知道他们有点依赖 "module": "commonjs",。有人可以尝试用最好的人类语言来解释它吗?

allowSyntheticDefaultImports州的官方文档

允许从模块进行默认导入而没有默认导出。这不影响代码发出,仅影响类型检查。

那是什么意思?如果没有任何导出默认值,我认为导入默认值的唯一用例就是初始化某些东西?喜欢单身人士吗?

以下问题/答案也没有意义, 是否可以在tsconfig中使用--esModuleInterop而不是将其作为标志?

--esModuleInterop在编译器页面上定义

发出__importStar和__importDefault帮助程序以实现运行时babel生态系统兼容性,并启用--allowSyntheticDefaultImports以实现类型系统兼容性。

我似乎也很难理解/理解

Krz*_*bek 40

问题陈述

当我们要将CommonJS模块导入ES6模块代码库时,会发生问题。

在这些标志之前,我们必须使用star(* as something)import 导入CommonJS模块:

// node_modules/moment/index.js
exports = moment
Run Code Online (Sandbox Code Playgroud)
// 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();
Run Code Online (Sandbox Code Playgroud)

我们可以看到它*在某种程度上等同于exports变量。它工作正常,但与es6模块规范不兼容。在规范中,星号导入中的名称空间记录(moment在我们的例子中)只能是一个普通对象,不能调用(moment()不允许)。

有了flag,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();
Run Code Online (Sandbox Code Playgroud)

它可以正常工作,并且在es6模块规范中完全有效,因为moment它不是星形导入的名称空间,而是默认导入。

但是它是如何工作的呢?如您所见,因为我们执行了默认导入,所以我们defaultmomentobject 上调用property 。但是我们没有在moment库中声明对象的任何default属性exports。该键__importDefault起作用。它将模块(exports)分配给defaultCommonJS模块的属性:

var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Run Code Online (Sandbox Code Playgroud)

如您所见,我们按原样导入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!
Run Code Online (Sandbox Code Playgroud)
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;
};
Run Code Online (Sandbox Code Playgroud)

合成进口

那又是allowSyntheticDefaultImports什么呢?现在文档应该很清楚:

Allow default imports from modules with no default export. This does not affect code emit, just typechecking.

moment键入中,我们没有指定默认导出,也没有指定默认导出,因为只有在标记esModuleInterop打开时才可用。因此allowSyntheticDefaultImports,如果我们要从没有默认导出功能的第三方模块中导入默认值,就不会报告错误。

  • 在引入“--esModuleInterop”标志之前,“import * as moment from 'moment'”是_不_正确的。`import moment = require('moment')` 是。 (4认同)

Tit*_*mir 5

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"));
Run Code Online (Sandbox Code Playgroud)

__importDefault:如果模块不是es模块,那么require返回的内容将成为默认值。这意味着,如果在commonjs模块上使用默认导入,则整个模块实际上是默认模块。

__importStar此PR中最好地描述了:

TypeScript将名称空间导入(即import * as foo from "foo")等同于const foo = require("foo")。事情在这里很简单,但是如果要导入的主要对象是基元或具有调用/构造签名的值,则无法解决。ECMAScript基本上说名称空间记录是一个普通对象。

Babel首先在模块中需要,然后检查名为的属性__esModule。如果__esModule设置为true,则其行为与TypeScript相同,但除此之外,它会合成一个名称空间记录,其中:

  1. 所有属性均从require'd模块中删除,并以命名导入的形式提供。
  2. 最初需要的模块作为默认导入可用。

所以我们得到这个:

// 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"));
Run Code Online (Sandbox Code Playgroud)

allowSyntheticDefaultImports是所有这些的伴侣,将其设置为false不会更改发出的帮助器(它们两者看起来仍然相同)。但是,如果您对commonjs模块使用默认导入,则会引发打字错误。因此import React from 'react'Module '".../node_modules/@types/react/index"' has no default export.如果allowSyntheticDefaultImportsis,则会引发错误false