esModuleInterop

TypeScript

以 React 的使用为例,在 commonjs 的环境下,React 的引用方式是:

const React = require('react');

而在 ES5 的语法中,React 官方使用的引用方式是:

import React from 'react';

(见 create-react-app 中生成的代码)

这就造成了一个问题:require('react') 这样的语法,对应的 import 语法应该怎么写?显然,require 语法无法直接转译成 import 语法:因为根据 import 的语法规则,import React from 'react'; 实际是将 default 引入,而 require('react') 的时候,并没有 .default 字段参与。

无论是 Webpack,Babel 还是 TypeScript,在这个问题上都采取了相同的策略,就是在将 import 转译成 require 语句的时候,多套一层:如果 require 的部分有 .default 字段,就使用这个字段;否则的话,就将整体当作是 .default 的值。

在 Webpack 中,使用的是 __webpack_require.n 这个函数:

__webpack_require__.n = function(module) {
  var getter = module && module.__esModule ?
    function getDefault() { return module['default']; } :
    function getModuleExports() { return module; };
  __webpack_require__.d(getter, 'a', getter);
  return getter;
};

对于 Babel,使用的是 _interopRequireDefault 这个函数:

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}
var react = _interopRequireDefault(require('react'));

对于 TypeScript 来说,在 compilerOptions 中增加 esModuleInterop 这个参数,就可以让 tsc 在编译 import 代码的时候进行一层包转转换,使用 __importDefault 确保无论是否是 ES6 module 的输出,都可以被正确的 require:

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

如果编译结果需要在 Node.js 环境下运行(如进行 Jest 单元测试等),可以考虑上面的配置方案。