Понимание esModuleInterop в файле tsconfig - PullRequest
12 голосов
/ 21 мая 2019

Я проверял кто-то .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 штатов

Разрешить импорт по умолчанию из модулей без экспорта по умолчанию. Это делает не влияет на генерацию кода, просто проверка типов.

Это значит? если нет экспортного значения по умолчанию, то я думаю, что единственным вариантом использования импортного значения по умолчанию будет инициализация чего-либо? как синглтон?

Следующий вопрос / ответ тоже не имеет смысла Есть ли способ использовать --esModuleInterop в tsconfig, а не флаг?

и --esModuleInterop определение на странице компилятора

Emit __importStar и __importDefault помощники для babel во время выполнения совместимость экосистемы и включить --allowSyntheticDefaultImports для совместимость систем типов.

Мне также было трудно понять / понять

Ответы [ 2 ]

6 голосов
/ 28 мая 2019

Постановка проблемы

Проблема возникает, когда мы хотим импортировать модуль CommonJS в кодовую базу модуля ES6.

До этих флагов нам приходилось импортировать модули CommonJS со звездочкой (* 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();

Мы можем видеть, что * было как-то эквивалентно exportsпеременная.Работало нормально, но не соответствовало спецификации модулей es6.В спецификации запись пространства имен при импортировании по звездам (в нашем случае moment) может быть только простым объектом, не подлежащим вызову (moment() не допускается).

Решение

С флагом esModuleInterop мы можем импортировать модули CommonJS в соответствии со спецификацией es6 модулей.Теперь наш код импорта выглядит следующим образом:

// 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 это не пространство имен из импорта по звездам, это импорт по умолчанию.

Нокак это устроено?Как видите, поскольку мы сделали импорт по умолчанию, мы вызываем свойство default для объекта moment.Но мы не объявили никакого свойства default для объекта exports в библиотеке моментов.Ключ находится в функции __importDefault.Он назначает модуль (exports) свойству default для модулей CommonJS:

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 просто не сообщит об ошибке, если мы хотим импортировать настройки по умолчанию из стороннего модуля, который не имеет экспорта по умолчанию.

1 голос
/ 28 мая 2019

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, то, что возвращается по запросу, становится значением по умолчанию.Это означает, что если вы используете импорт по умолчанию для модуля commonjs, весь модуль фактически является модулем по умолчанию.

__importStar лучше всего описать в этом PR :

TypeScript рассматривает импорт пространства имен (т. Е. import * as foo from "foo") как эквивалент const foo = require("foo").Здесь все просто, но они не срабатывают, если импортируемый первичный объект является примитивом или значением с сигнатурами вызова / конструкции.ECMAScript в основном говорит, что запись пространства имен является простым объектом.

Сначала Babel требует в модуле и проверяет свойство с именем __esModule.Если для __esModule установлено значение true, то поведение такое же, как и для TypeScript, но в противном случае он синтезирует запись пространства имен, в которой:

  1. Все свойства извлекаются из запроса 'Модуль 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"));

allowSyntheticDefaultImports является компаньоном для всего этого, установка этого значения в false не изменит выброшенных помощников (они оба будут выглядеть одинаково).Но это вызовет ошибку машинописного текста, если вы используете импорт по умолчанию для модуля commonjs.Так что import React from 'react' выдаст ошибку Module '".../node_modules/@types/react/index"' has no default export., если allowSyntheticDefaultImports равно false.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...