Typescript配置文件(tsconfig.json)详解系列四:esModuleInterop和allowSyntheticDefaultImports

news2024/9/9 0:45:04

Typescript版本

Typescript5.5.2

如果我们使用ESM作为模块系统,那么我们经常会用以下两种方式去导入另一个模块:

// 引入一个对象,包括了所有的export xxx 和 export default
import * as A from './xx';
// 引入export default
import B from './xx';

转换为Commonjs的写法为:

const A = require('xx');
const B = require('xx').default;

但是实际上这种转换并不是完全对等的,因为Commonjs中并没有default(实际上大部分模块系统都没有default)。这种不对等会导致Typescript编译后的js文件无法正常执行。

问题:import X from 'xx'

我们将esModuleInterop设置为false来模拟一下这些问题

{
    compilerOptions: {
        "esModuleInterop": false
    }
}

有如下两个模块分别使用ESM和Commonjs进行导出:

// cjs_default.ts
// Typescript的Commonjs导出写法
export = {
    name: 1
}
// esm_default.ts
// ESM导出
export default {
    name: 'name'
}

然后我们使用ESM来引入这两个模块

import esm from './esm_default';
import cjs from './cjs_default';

console.log(cjs, esm);

此时编译会报错,原因就是我们之前提到的Commonjs没有默认导出:

ts/index.ts:2:8 - error TS1259: Module '"C:/Users/86159/WebstormProjects/\u9762\u8BD5\u9898/ts/cjs_default"' can only be default-imported using the 'esModuleInterop' flag

2 import cjs from './cjs_default';
         ~~~

  ts/cjs_default.ts:1:1
    1 export = {
      ~~~~~~~~~~
    2     name: 1
      ~~~~~~~~~~~
    3 }
      ~
    This module is declared with 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.


Found 1 error in ts/index.ts:2


Process finished with exit code 2

这个报错虽然让我们去开启esModuleInterop,但是实际上我们只需要将allowSyntheticDefaultImports这个字段设置为true就好了。

现在代码可以正常编译了:

// cjs_default.js
// 被正常编译为commonjs模块了
module.exports = {
    name: 1
};

// esm_default.js
// 这个exports.__esModule 标识了这个commonjs模块是由ESM模块编译转换的。
Object.defineProperty(exports, "__esModule", { value: true });
// module.exports可以省略module
exports.default = {
    name: 'name'
};
// index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var esm_default_1 = require("./esm_default");
var cjs_default_1 = require("./cjs_default");
console.log(cjs_default_1.default, esm_default_1.default);

我们执行一下生成的js代码,但是我们并没有正常的从Commonjs导出的模块中得到正确的结果(获得了undefined),虽然我们的类型检查通过了,代码也编译了,但是并没有编译成我们期望的结果。

undefined { name: 'name' }

我们将esModuleInterop设置为true之后代码可以正常编译了,我们看一下编译后的文件:

// cjs_default.js
// 被正常编译为commonjs模块了
module.exports = {
    name: 1
};

// esm_default.js
// 这个exports.__esModule 标识了这个commonjs模块是由ESM模块编译转换的。
Object.defineProperty(exports, "__esModule", { value: true });
// module.exports可以省略module
exports.default = {
    name: 'name'
};
// index.js
// 开启esModuleInterop为true会在编译文件中生成这个函数,如果在转化import A from 'xx'时调用。
// 对没有default的Commonjs模块系统封装一层(通过有没有__esModule字段判断需不需要封装)     
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var esm_default_1 = __importDefault(require("./esm_default"));
var cjs_default_1 = __importDefault(require("./cjs_default"));
console.log(cjs_default_1.default, esm_default_1.default);

 我们执行一下生成的js代码,这次可以正确的输出了:

{ name: 1 } { name: 'name' }

问题:import * as X from 'xx'

{
    compilerOptions: {
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
    }
}

index.ts代码如下:

import * as esm from './esm_default';
import * as cjs from './cjs_default';

console.log(cjs, esm);

 编译一直报类型检查的错误,报错如下:

ts/index.ts:2:22 - error TS2497: This module can only be referenced with ECMAScript imports/exports by turning on the 'esModuleInterop' flag and referencing its default export.

2 import * as cjs from './cjs_default';
                       ~~~~~~~~~~~~~~~


Found 1 error in ts/index.ts:2

 但是编译后的代码是可以执行的,执行结果如下,感觉还是有点奇怪的:

{ name: [Getter], default: { name: 1 } } { default: { name: 'name' } }

 编译后的代码可以看到,也是注入了转换函数(__importStar 等

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const esm = __importStar(require("./esm_default"));
const cjs = __importStar(require("./cjs_default"));
console.log(cjs, esm);

问题:import {x} from 'xx'

index.ts代码如下:

import {
    name
} from './cjs_default';

console.log(name);

会报和import * as X from 'xx'一模一样的错误, 但是编译后的代码是可以执行的,执行结果如下:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cjs_default_1 = require("./cjs_default");
console.log(cjs_default_1.name);

 编译后的代码如下:

总结

allowSyntheticDefaultImports

  • allowSyntheticDefaultImports可以让这种使用ESM模块系统导入default时,目标模块系统没有default的类型检查通过(只影响类型检查,不影响编译结果)

esModuleInterop

  • esModuleInterop设置为true的时候,allowSyntheticDefaultImports如果没有定义,会自动跟着设置为true。
  • esModuleInterop会自动注入工具函数,将没有default的模块系统封装一层,让使用ESM模块系统导入default的结果符合预期。

使用ESM模块系统引入Commonjs模块

方式是否支持备注
import X from 'xx'支持
import * as X from 'xx'不支持报错,能正常编译
import {x} from 'xx'不支持报错,能正常编译

import * as X from 'xx',这种还有工具函数__importStar ,而且官方文档对esModuleInterop的讲解还举了这个例子但是会类型报错很不理解

如果想解决这个问题有几个方案:

1. 使用ts的校验错误忽略@ts-ignore

2. 改写export = 为 export,使用ESM模块系统

export = {
    name: 1
}

// 改写为
const name = 1;
export {
   name
}

// 或者改写为
export default {
  name
}

// 或者改写为
export const name = 1;

3. 如果是引入的是js文件,那可以通过定义d.ts文件避免类型检验错误(虽然js中使用的是commonjs模块系统,但是d.ts定义成ESM模块系统)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1958667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五、Spring Boot - 上手篇(1)

🌻🌻目录 一、快速入门:创建第一个SpringBoot 工程1.1 点击File--->New--->Project...1.2 选择版本和依赖的相关骨架包1.3 设置项目保存目录1.4 项目创建完成,工程主界面如下1.5 项目说明1.6 启动项目1.7 编写 HelloControl…

2024年7月23日~2024年7月29日周报

目录 一、前言 二、完成情况 2.1 一种具有边缘增强特点的医学图像分割网络 2.2 融合边缘增强注意力机制和 U-Net 网络的医学图像分割 2.3 遇到的困难 三、下周计划 一、前言 上周参加了一些师兄师姐的论文讨论会议,并完成了初稿。 本周继续修改论文&#xff0…

【Git】 如何将一个分支的某个提交合并到另一个分支

【Git】 如何将一个分支的某个提交合并到另一个分支 在使用 Git 进行版本控制时,常常会遇到这样的需求:将某个分支的特定提交合并到另一个分支中。这种情况下,我们可以使用 cherry-pick 命令来实现。本文将详细介绍 cherry-pick 命令的使用方…

定时任务报错问题,通级下还有其他定时任务却没有报错,如何解决??

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收…

R语言统计分析——条形图

参考资料:R语言实战【第2版】 条形图通过垂直的或水平的条形展示了类别型变量的分布(频数)。函数barplot()的最简单用法是: barplot(height) 其中height是一个向量或一个矩阵。 本文中数据案例来自“vcd”包,需在第一…

JQuery异步请求与Flask后端通信、this和event指针汇总

目录 一.JQuery与Flask通信的三种方法 1.1$.ajax() 1.2$.get() 1.3$.post() 二.forEach()方法 三.this指针 3.1为什么要用this指针 3.2this的指向 3.3this指针的四种绑定方式 3.3.1默认绑定 3.3.2隐式绑定 3.3.3显式绑定 3.3.4new绑定 3.3.5通过标签调用this指针…

windows server服务器/linux服务器离线安装pandas

windows server服务器/linux服务器离线安装pandas pypi官网下载whl文件速度较慢,推荐使用国内的镜像源来下载,镜像源地址为 清华大学 :https://pypi.tuna.tsinghua.edu.cn/simple/ 阿里云:http://mirrors.aliyun.com/pypi/simple…

docker安装与container基本使用

安装 Homebrew 的 Cask 已经支持 Docker for Mac, mac用户狂喜 brew install --cask --appdir/Applications docker其他入门用法可参考 Docker Hello World- 菜鸟教程 或网上自行搜索博客学习。本文主要记录我运行go-zero-mall用到的一些注意点。当然,gonivinck项…

vue2+cesium实现报警特效

实现效果: 录制_2024_07_29_21_36_33_571 实现步骤如下: 1、安装cesium包:npm install cesium 2、在node-modules中找到刚刚安装的cesium,在文件夹中里面有一个Build文件夹,把Build里面的Cesium复制出来丢到public中 3、在index.html中进行引入: 4、新建一个js文件,…

前端常见场景、JS计算精度丢失问题(Decimal.js 介绍)

目录 一. Decimal.js 介绍 二. 常用方法 1. 创建 Decimal 实例 2.加法 add 或 plus 3.减法 sub 或 minus 4.乘法 times 或 mul 5.除法 div 或 dividedBy 6.取模 7.幂运算 8.平方根 9.保留小数位 toFixed方法(四舍五入) 三.项目应用 前端精度丢失问题通常由以下原因…

无线融合,智能协同:ECWAN打造物联网高效协同网络

关键词:资产管理、ECWAN、无线混合组网、用电监测、用电计量、广域基站、计量插座、边缘协同 在当今快速发展的物联网领域,每天都有数以亿计的设备互相通信,形成庞大而复杂的网络生态。在这样的背景下,ECWAN技术以其灵活、高效和智…

【Linux】TCP全解析:构建可靠的网络通信桥梁

文章目录 前言1. TCP 协议概述2. TCP报头结构3. 如何理解封装和解包呢?4. TCP的可靠性机制4.1 TCP的确认应答机制 4.2 超时重传机制5. TCP链接管理机制5.1 经典面试题:为什么建立连接是三次握手?5.2 经典面试题:为什么要进行四次挥…

读零信任网络:在不可信网络中构建安全系统04最小特权

1. 公钥基础设施 1.1. PKI 1.2. 数字证书本身并不能解决身份认证问题 1.2.1. 需要一种方法来验证获得的公钥的确属于某人 1.2.2. 公钥基础设施(PKI)解决了这个问题 1.3. PKI定义了一组角色及其职责,能够在不可信的网络中安全地分发和验证…

【Websim.ai】一句话让AI帮你生成一个网页

【Websim.ai】一句话让AI帮你生成一个网页 网站链接 websim.ai 简介 websim.ai接入了Claude Sonnet 3.5,GPT-4o等常用的LLM,只需要在websim.ai的官网指令栏中编写相关指令,有点类似大模型的Prompt,指令的好坏决定了网页生成的…

Lc63---1859将句子排序(排序)---Java版(未写完)

1.题目描述 2.思路 (1)首先将句子按空格分割成若干单词。 (2)每个单词的最后一个字符是它的位置索引。我们可以通过这个索引将单词恢复到正确的位置。 (3)按照单词的索引顺序排序这些单词。 (4…

【已解决】嵌入式linux mobaxterm unable to open connection to comx 串口正常连接,但终端无法输入

1.点击Session重新选择串口,注意看看串口是不是连接到虚拟机,导致串口被占用。 2.选择PC机与开发板连接的串口,不知道的话可以打开设备管理器看看,选择正确的波特率,一般是115200。 3.关键一步:选择后别急…

性能测试:深入探索与实战指南

大家好,我是一名测试开发工程师,已经开源一套【自动化测试框架】和【测试管理平台】,欢迎大家联系我,一起【分享测试知识,交流测试技术】 在当今这个信息化、数字化的时代,软件系统的性能直接关乎到用户体验…

使用 Matlab 绘制带有纹理的柱状图

以下是效果 1. 在 Matlab 里安装两个额外的库: hatchfill2 和 legendflex。 (1)搜索并安装 hatchfill2,用来画纹理 (2) 搜索并安装 legendflex,用来画自定义的图例 2. 代码(说明见注释) data …

Centos 7系统(最小化安装)安装Git 、git-man帮助、补全git命令-详细文章

安装之前由于是最小化安装centos7安装一些开发环境和工具包 文章使用国内阿里源 cd /etc/yum.repos.d/ && mkdir myrepo && mv * myrepo&&lscurl -O https://mirrors.aliyun.com/repo/epel-7.repo;curl -O https://mirrors.aliyun.com/repo/Centos-7…

docker安装phpMyAdmin

直接安装phpMyAdmin需要有php环境,比较麻烦,总结了使用docker安装方法,并提供docker镜像。 1.docker镜像 见我上传的docker镜像:https://download.csdn.net/download/taotao_guiwang/89595177 2.安装 1).加载镜像 docker load …