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