Grundlegendes zu esModuleInterop in der Datei tsconfig


140

Ich habe gerade eine .tsconfigDatei ausgecheckt und dort entdeckt--esModuleInterop

Dies ist seine .tsconfigAkte

{
  "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"]
}

Hier ist meine Hauptfrage, was ist "esModuleInterop": true,und "allowSyntheticDefaultImports": true,. Ich weiß, dass sie irgendwie von der abhängig sind "module": "commonjs",. Kann jemand versuchen, es in der bestmöglichen menschlichen Sprache zu erklären?

Die offiziellen Dokumente für allowSyntheticDefaultImportsStaaten

Standardimporte aus Modulen ohne Standardexport zulassen. Dies wirkt sich nicht auf die Code-Ausgabe aus, sondern nur auf die Typüberprüfung.

Bedeutet das? Wenn es keinen Exportstandard gibt, besteht der einzige Anwendungsfall des Importstandards darin, etwas zu initialisieren. wie Singleton?

Die folgende Frage / Antwort ist ebenfalls nicht sinnvoll. Gibt es eine Möglichkeit, --esModuleInterop in tsconfig zu verwenden, anstatt ein Flag zu sein?

Und --esModuleInteropDefinition auf der Compilerseite

Geben Sie die Hilfsprogramme __importStar und __importDefault für die Kompatibilität des Laufzeit-Babel-Ökosystems aus und aktivieren Sie --allowSyntheticDefaultImports für die Typsystemkompatibilität.

Auch schien mir schwer zu verstehen / zu verstehen

Antworten:


166

Problemstellung

Das Problem tritt auf, wenn das CommonJS-Modul in die Codebasis des ES6-Moduls importiert werden soll.

Vor diesen Flags mussten wir CommonJS-Module mit star ( * as something) import importieren:

// 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();

Wir können sehen, dass *das irgendwie gleichbedeutend mit exportsVariable war. Es hat gut funktioniert, war aber nicht mit den Spezifikationen der es6-Module kompatibel. In der Spezifikation kann der Namespace-Datensatz beim Sternimport ( momentin unserem Fall) nur ein einfaches Objekt sein, das nicht aufgerufen werden kann ( moment()ist nicht zulässig).

Lösung

Mit flag können esModuleInteropwir CommonJS-Module gemäß der Modulspezifikation importieren es6. Jetzt sieht unser Importcode folgendermaßen aus:

// 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();

Es funktioniert und ist mit der es6-Modulspezifikation perfekt gültig, da momentes sich nicht um einen Namespace aus dem Sternimport handelt, sondern um einen Standardimport.

Aber wie funktioniert es? Wie Sie sehen können, rufen wir, da wir den Standardimport durchgeführt haben, die defaultEigenschaft für das momentObjekt auf. Wir haben jedoch keine defaultEigenschaft für das exportsObjekt in der Moment-Bibliothek deklariert . Der Schlüssel ist in __importDefaultFunktion. Es weist exportsder defaultEigenschaft für CommonJS-Module die Eigenschaft module ( ) zu :

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

Wie Sie sehen können, importieren wir es6-Module so wie sie sind, aber CommonJS-Module werden mit einem defaultSchlüssel in ein Objekt eingeschlossen . Dies ermöglicht den Import von Standardeinstellungen in CommonJS-Module.

__importStarerledigt den ähnlichen Job - es gibt unberührte esModules zurück, übersetzt aber CommonJS-Module in Module mit der defaultEigenschaft:

// 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;
};

Synthetische Importe

Und was ist allowSyntheticDefaultImports, wofür ist es? Jetzt sollten die Dokumente klar sein:

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

In momentTypisierungen haben wir keinen Standardexport angegeben und sollten dies auch nicht tun, da er nur mit aktiviertem Flag verfügbar ist esModuleInterop. So allowSyntheticDefaultImportswird nicht nur Fehler melden , wenn wir von thrid Party Modul zu importieren Standard verwenden möchten , die keine Standard - Export haben.


Vor der Einführung der --esModuleInteropFlagge import * as moment from 'moment'war nicht korrekt. import moment = require('moment')war.
— Aluan Haddad vor

14

esModuleInteropgeneriert die in den Dokumenten beschriebenen Helfer. Wenn wir uns den generierten Code ansehen, können wir genau sehen, was diese tun:

//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: Wenn das Modul kein esModul ist, wird das, was von require zurückgegeben wird, zum Standard. Dies bedeutet, wenn Sie den Standardimport für a verwendencommonjs Modul das gesamte Modul der Standard ist.

__importStarwird am besten in dieser PR beschrieben :

TypeScript behandelt einen Namespace-Import (dh import * as foo from "foo") als äquivalent zuconst foo = require("foo") . Die Dinge sind hier einfach, aber sie funktionieren nicht, wenn das zu importierende primäre Objekt ein Grundelement oder ein Wert mit Aufruf- / Konstruktsignaturen ist. ECMAScript sagt im Grunde, dass ein Namespace-Datensatz ein einfaches Objekt ist.

Babel benötigt zuerst im Modul und sucht nach einer Eigenschaft mit dem Namen __esModule. Wenn auf gesetzt __esModuleist true, ist das Verhalten das gleiche wie das von TypeScript, ansonsten wird ein Namespace-Datensatz synthetisiert, in dem:

  1. Alle Eigenschaften werden aus dem erforderlichen Modul entfernt und als benannte Importe verfügbar gemacht.
  2. Das ursprünglich benötigte Modul wird als Standardimport zur Verfügung gestellt.

Also bekommen wir folgendes:

// 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"));

allowSyntheticDefaultImportsWenn Sie dies auf false setzen, werden die ausgegebenen Helfer nicht geändert (beide sehen immer noch gleich aus). Es wird jedoch ein Typoskriptfehler ausgelöst, wenn Sie den Standardimport für ein commonjs-Modul verwenden. Dies import React from 'react'löst also den Fehler aus, Module '".../node_modules/@types/react/index"' has no default export.wenn dies der Fall allowSyntheticDefaultImportsist false.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.