Skip to content

Latest commit

 

History

History
88 lines (62 loc) · 3.17 KB

README.md

File metadata and controls

88 lines (62 loc) · 3.17 KB

esModuleInterop

It's confusing. It's weird.

Maybe this will make it simpler.

See it in action

Run yarn. Then run yarn esm-true, and yarn esm-false. Check the console.

What's the difference? It's just a function that typescript creates. You can see it in the compiled esm.js files that are created by running the above commands.

With esModuleInterop: false:

Source

import thing from './commonjs/old-esm';
import thing2 from './commonjs/commonjs';

console.log('"esModuleInterop": false \n');
console.log(`The default export of a commonjs file that used to be esm: "${thing}".\n It works! \n`);
console.log(`The default export of a commonjs file: "${thing2}".\n It doesn't work :(. The original commonjs file never had the concept of a default export.\n`);

Compiled

// Typescript marks the file with "__esModule" to indicate that the file used to be esm
Object.defineProperty(exports, "__esModule", { value: true });
var old_esm_1 = require("./commonjs/old-esm");
var commonjs_1 = require("./commonjs/commonjs");
console.log('"esModuleInterop": false \n');
console.log("The default export of a commonjs file that used to be esm: \"" + old_esm_1.default + "\".\n It works! \n");
console.log("The default import of a commonjs file: \"" + commonjs_1.default + "\".\n It doesn't work :(\n");

All Typescript knows is that, in the source (.ts) file, we're asking for the default export. So it naively adds .default at the end of the imports, not knowing that the commonjs file doesn't have a default export.

With esModuleInterop: true:

Source

Same code as before:

import thing from './commonjs/old-esm';
import thing2 from './commonjs/commonjs';

console.log('"esModuleInterop": true \n');
console.log(`The default export of a commonjs file that used to be esm: "${thing}".\n It works! \n`);
console.log(`The default export of a commonjs file: "${thing2}".\n It works!\n`);

Compiled

var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var old_esm_1 = __importDefault(require("./commonjs/old-esm"));
var commonjs_1 = __importDefault(require("./commonjs/commonjs"));
console.log('"esModuleInterop": true \n');
console.log("The default export of a commonjs file that used to be esm: \"" + old_esm_1.default + "\".\n It works! \n");
console.log("The default import of a commonjs file: \"" + commonjs_1.default + "\".\n It works!\n");

The big difference: __importDefault. Typescript creates this simple method, which simply checks whether the module used to be an __esModule. If it was, it simply returns the module. But if the file was originally commonjs (wasn't an __esModule), typescript takes the module and wraps it with: { "default": mod }.

So now we can be sure that module.default is defined no matter what.

Simplified a little:

const importDefault = function (module) {
  if (module.__esModule) {
    return module;
  } else {
    return { 'default': module }
  }
};

const myImportedModule = importDefault(require("./some-module"));
// myImportedModule = { default: module_code }