详解 TypeScript 内置的模块导入兼容方式

作者:木棉

时间:2026-10-23

浏览:227

本文深入解析 TypeScript 在处理 CommonJS 与 ES6 模块混用时的兼容机制。重点介绍 esModuleInterop 和 allowSyntheticDefaultImports 配置项的作用差异,以及如何通过正确配置实现 Webpack 的 Tree Shaking 优化,解决 React 等库导入报错问题。

文章目录

前言

一、前言

前端模块化规范主要包含 CommonJS、AMD、CMD 以及 ES6。其中 AMD 和 CMD 属于过渡时期的产物,当前主流应用的是 CommonJS 和 ES6。在 TypeScript 项目中混合使用这两种模块化方案时,经常引发一些难以预料的错误。

import * as 的使用

二、import * as

为了兼顾兼容性,通常需要将代码编译为 ES5 标准,因此 tsconfig.json 中常包含如下配置:


{
 "compilerOptions": {
  "module": "commonjs",
  "target": "es5",
 }
}

代码编译后最终将以 CommonJS 格式输出。在使用 React 时,若采用 import React from "react" 这种写法,往往会收到一个令人困惑的报错:


Module "react" has no default export

此时只能将代码修改为 import * as React from "react"。其根本原因在于,React 是以 CommonJS 规范进行导出的,而 import React from "react" 这种语法会尝试查找 React 模块中的 exports.default 属性,由于 React 并未导出该属性,从而引发上述错误。相比之下,import * as React 的写法会直接获取 module.exports 的值,这样使用便不会出现问题。我们来看看 React 模块导出的具体代码(经过精简):


...
var React = {
 Children: {
  map: mapChildren,
  forEach: forEachChildren,
  count: countChildren,
  toArray: toArray,
  only: onlyChild
 },

 createRef: createRef,
 Component: Component,
 PureComponent: PureComponent,
 ...
}

module.exports = React;

从代码可以看出,React 导出的是一个对象,自然不存在 default 属性。

esModuleInterop 配置解析

二、esModuleInterop

为了解决此类兼容性问题,TypeScript 提供了 esModuleInterop 和 allowSyntheticDefaultImports 这两个配置项,启用后即可避免报错:


{
 "compilerOptions": {
  "module": "commonjs",
  "target": "es5",
  "allowSyntheticDefaultImports": true,
  "esModuleInterop": true
 }
}

其中,allowSyntheticDefaultImports 字段的作用仅是在静态类型检查阶段,忽略那些没有 exports.default 的导入报错。而 esModuleInterop 则会在编译过程中真正生成兼容代码,确保模块能够正确导入。还是之前的代码:


import React from "react";

现在 TypeScript 编译后的结果如下:


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"));

编译器自动生成了一个新对象,将模块赋值给其 default 属性,从而在运行时不再报错。

Tree Shaking 与组件按需加载

三、Tree Shaking

如果将 TypeScript 按照 ES6 规范进行编译,则无需启用 esModuleInterop,只需配置 allowSyntheticDefaultImports 即可防止静态类型检查时报错。


{
 "compilerOptions": {
  "module": "es6",
  "target": "es6",
  "allowSyntheticDefaultImports": true
 }
}

什么情况下我们会考虑导出成 ES6 规范呢?多数情况是为了使用 webpack 的 tree shaking 特性,因为它仅对 ES6 代码生效。

顺便再发散一下,讲讲 babel-plugin-component。


import { Button, Select } from 'element-ui'

上面的代码经过编译后,结果如下:


var a = require('element-ui'); 
var Button = a.Button; 
var Select = a.Select;
var a = require('element-ui') 会引入整个组件库,即使只用了其中的 2 个组件。
babel-plugin-component 的作用是将代码做如下转换:

// 转换前
import { Button, Select } from 'element-ui'
// 转换后
import Button from 'element-ui/lib/button' 
import Select from 'element-ui/lib/select'

最终编译出来的代码是这样的,只会加载用到的组件:


var Button = require('element-ui/lib/button');
var Select = require('element-ui/lib/select');

总结

四、总结

本文讲解了 TypeScript 是如何导入不同模块标准打包的代码的。无论你导入的是 commonJS 还是 ES6 的代码,万无一失的方式是把 esModuleInterop 和 allowSyntheticDefaultImports 都配置上。

到此这篇关于详解Typescript 内置的模块导入兼容方式的文章就介绍到这了,更多相关Typescript 内置模块导入兼容内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章