Noções básicas sobre esModuleInterop no arquivo tsconfig


142

Eu estava verificando o .tsconfigarquivo de alguém e lá encontrei--esModuleInterop

Este é o .tsconfigarquivo dele

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

Aqui, minha pergunta principal é o que é "esModuleInterop": true,e "allowSyntheticDefaultImports": true,. Eu sei que eles são meio que dependentes do "module": "commonjs",. Alguém pode tentar explicá-lo na melhor linguagem humana possível?

Os documentos oficiais dos allowSyntheticDefaultImportsestados

Permitir importações padrão de módulos sem exportação padrão. Isso não afeta a emissão de código, apenas a verificação de digitação.

Isso significa? se não houver nenhum padrão de exportação, então acho que o único caso de uso do padrão de importação seria inicializar algo? como singleton?

A pergunta / resposta a seguir também não faz sentido. Existe uma maneira de usar --esModuleInterop em tsconfig em vez de ser um sinalizador?

E a --esModuleInteropdefinição na página do compilador

Emita ajudantes __importStar e __importDefault para compatibilidade de ecossistema babel em tempo de execução e habilite --allowSyntheticDefaultImports para compatibilidade de sistema de tipos.

Também parecia difícil para mim entender / compreender

Respostas:


171

Declaração do problema

O problema ocorre quando queremos importar o módulo CommonJS para a base de código do módulo ES6.

Antes dessas sinalizações, tínhamos que importar módulos CommonJS com star ( * as something) import:

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

Podemos ver que *era de alguma forma equivalente a exportsvariável. Funcionou bem, mas não era compatível com as especificações dos módulos es6. Nas especificações, o registro de namespace na importação estrela ( momentem nosso caso) pode ser apenas um objeto simples, não pode ser chamado ( moment()não é permitido).

Solução

Com o sinalizador esModuleInterop, podemos importar módulos CommonJS em conformidade com as es6especificações dos módulos. Agora, nosso código de importação se parece com este:

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

Funciona e é perfeitamente válido com as especificações dos módulos es6, porque momentnão é um namespace da importação estrela, é importação padrão.

Mas como funciona? Como você pode ver, como fizemos importação padrão, chamamos de defaultpropriedade no momentobjeto. Mas não declaramos nenhuma defaultpropriedade no exportsobjeto na biblioteca de momentos. A chave está em __importDefaultfuncionamento. Ele atribui module ( exports) à defaultpropriedade para módulos CommonJS:

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

Como você pode ver, importamos módulos es6 como estão, mas os módulos CommonJS são agrupados em um objeto com a defaultchave. Isso possibilita a importação de padrões em módulos CommonJS.

__importStarfaz o trabalho semelhante - retorna esModules intocados, mas traduz os módulos CommonJS em módulos com a defaultpropriedade:

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

Importações sintéticas

E para allowSyntheticDefaultImportsque serve? Agora, os documentos devem ser claros:

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

Nas momenttipificações, não especificamos a exportação padrão, e não deveríamos, pois está disponível apenas com o sinalizador esModuleInteropativado. Portanto allowSyntheticDefaultImports, não reportamos erro se quisermos importar o padrão de um módulo de terceiros que não tem exportação padrão.


Antes da introdução da --esModuleInteropbandeira, nãoimport * as moment from 'moment' estava correto. foi. import moment = require('moment')
Aluan Haddad

14

esModuleInteropgera os auxiliares descritos nos documentos. Olhando para o código gerado, podemos ver exatamente o que eles fazem:

//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: Se o módulo não for um esmódulo, o que é retornado por require torna-se o padrão. Isso significa que se você usar a importação padrão em um commonjsmódulo, todo o módulo será, na verdade, o padrão.

__importStaré melhor descrito neste PR :

O TypeScript trata uma importação de namespace (ou seja import * as foo from "foo") como equivalente a const foo = require("foo"). As coisas são simples aqui, mas não funcionam se o objeto primário sendo importado for um primitivo ou um valor com assinaturas de chamada / construção. ECMAScript basicamente diz que um registro de namespace é um objeto simples.

O Babel primeiro requer no módulo e verifica se há uma propriedade chamada __esModule. Se __esModuleestiver definido como true, o comportamento é o mesmo do TypeScript, mas, caso contrário, ele sintetiza um registro de namespace onde:

  1. Todas as propriedades são retiradas do módulo requerido e disponibilizadas como importações nomeadas.
  2. O módulo requerido originalmente é disponibilizado como uma importação padrão.

Então, nós entendemos:

// 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é o companheiro de tudo isso, definir como false não mudará os auxiliares emitidos (ambos ainda terão a mesma aparência). Mas isso irá gerar um erro de digitação se você estiver usando a importação padrão para um módulo commonjs. Portanto, isso import React from 'react'gerará o erro Module '".../node_modules/@types/react/index"' has no default export.se allowSyntheticDefaultImportsfor false.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.