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

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' flag2 import cjs from './cjs_default';~~~ts/cjs_default.ts:1:11 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:2Process 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.mzph.cn/news/877262.shtml

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

相关文章

自定义表格_可拖拽排序

在做后台管理系统的时候,经常需要表格里面的每行排序,自定义可拖拽表格,更改样式方便。 一、实现效果 进行拖拽演示: 可拖拽排序表格 无滚动条样式: 有滚动条样式: 二、代码 使用reactscssts,实现页面。 …

Linux(CentOS)ftp服务搭建

ftp服务器搭建 1. 下载ftp服务2. 查找ftp配置文件3. 查看配置文件信息4. Windows连接ftp服务1)使用文件资源管理器连接2)使用FlashFXP工具,比文件资源管理器好用,强烈推荐 5. Linux连接 1. 下载ftp服务 yum install -y vsftpd2. …

Docsify:快速用Markdown文档搭建网站的利器

Github官方地址:Docsify 什么是Docsify? 对于经常写博客的人来说,markdown大家都不陌生。今天介绍一个在最近需求中碰到的软件Docsify,通过它能够将Markdown直接转换为网页。话不多说,下面直接介绍它的快速用法。 D…

Apache、nginx

一、Web 1、概述 Web:为⽤户提供的⼀种在互联⽹上浏览信息的服务,Web 服务是动态的、可交互的、跨平台的和图形化的。 Web 服务为⽤户提供各种互联⽹服务,这些服务包括信息浏览服务,以及各种交互式服务,包括聊天、购物…

fastapi教程(五):中间件

一,什么是中间件 中间件是一种软件组件,它在请求到达应用程序处理程序之前和/或响应发送回客户端之前执行操作。 请求从客户端发出。 请求首先经过Middleware 1。 然后经过Middleware 2。 请求到达FastAPI路由处理器。 响应从路由处理器返回。 响应经过…

如何通过 CloudCanal 实现从 Kafka 到 AutoMQ 的数据迁移

01 引言 随着大数据技术的飞速发展,Apache Kafka 作为一种高吞吐量、低延迟的分布式消息系统,已经成为企业实时数据处理的核心组件。然而,随着业务的扩展和技术的发展,企业面临着不断增加的存储成本和运维复杂性问题。为了更好地…

《LeetCode热题100》---<双指针篇四道>

本篇博客讲解LeetCode热题100道双指针篇中的 第一道:移动零(简单) 第二道:盛最多水的容器(中等) 第一道:移动零(简单) class Solution {public void moveZeroes(int[] nu…

基于CentOS Stream 9平台安装JDK17.0.12

官方: https://www.oracle.com/java/technologies/downloads/#java17 1. 下载: https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz 2. 存放目录 mkdir /usr/local/javacd /usr/local/java3. 解压 tar -zxvf jdk-17_linux-x64_…

除了GPT,还有哪些好用的AI工具?

最强AI视频生成:小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 多得很,这20个免费的国产AI工具,打工人必备,除了比chatGPT好用,甚至还可以用来变现…

C语言中的指针基础

文章目录 🍊自我介绍🍊地址🍊C语言中的指针 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞关注评论收藏(一键四连)哦~ 🍊自我介绍 Hello,大家好,我是小珑也要变强&am…

论文阅读-《Distant Supervision for Relation Extraction beyond the Sentence Boundary》

文章提出了首个将远程监督应用于跨句子关系提取的方法,通过整合句内和句间关系,利用图表示和多路径特征提取提高了准确性和鲁棒性。 摘要 文章提出了一种新的方法,用于在远程监督下进行跨句子的关系抽取。这种方法利用图表示来整合依赖和话…

常用传感器讲解十五--触摸传感器(KY-036)

常用传感器讲解十五–触摸传感器(KY-036) 具体讲解 这个比较简单,就是触摸后给个信号 电路连接 在Arduino上将VCC引脚连接到5V。 将GND连接到Arduino的GND。 将OUT连接到Arduino上的D2 代码实现 void setup() {pinMode(2, INPUT);Seri…

spark 3.0.0源码环境搭建

环境 Spark版本:3.0.0 java版本:1.8 scala版本:2.12.19 Maven版本:3.8.1 编译spark 将spark-3.0.0的源码导入到idea中 执行mvn clean package -Phive -Phive-thriftserver -Pyarn -DskipTests 执行sparksql示例类SparkSQLExam…

Kotlin 的优势:现代编程语言的卓越选择

文章目录 简洁与优雅的语法空安全特性函数式编程,支持高阶函数、lambdaKotlin 内联函数与 Java 的互操作性强大的类型推断协程支持lazy 委托object 单例模式区间表达式现代的开发工具支持 本文首发地址 https://h89.cn/archives/301.html 最新更新地址 https://gite…

科学设计程序员面试内容,破解“八股文”之弊

“八股文”在实际工作中是助力、阻力还是空谈? 作为现在各类大中小企业面试程序员时的必问内容,“八股文”似乎是很重要的存在。但“八股文”是否能在实际工作中发挥它“敲门砖”应有的作用呢?有IT人士不禁发出疑问:程序员面试考…

Lombok注解之@SneakyThrows作用

Lombok注解之SneakyThrows作用 读法 [ˈsniːki] [θroʊz] 悄悄的 抛出顾名思义,它能够自动偷摸的为咱们的代码生成一个try…catch块,并把异常向上抛出来。 使用 SneakyThrows的使用范围: 只能作用在方法和构造函数之上。从源码就可以…

C# dataGridView 去掉左边多出来空列

1.问题 在使用winform做界面程序时,dataGridView控件创建好后,左侧会多出一列为空,如何删除呢 2.解决方法 你可以在属性窗口中进行设置 如图: 将RowHeadersVisible 属性设置为False 或者代码设置 this.dataGridView1.RowHea…

我们的前端开发逆天了!1 小时搞定了新网站,还跟我说 “不要钱”

大家好,我是程序员鱼皮。前段时间我们上线了一个新软件 剪切助手 ,并且针对该项目做了一个官网: 很多同学表示官网很好看,还好奇是怎么做的,其实这个网站的背后还有个有趣的小故事。。。 鱼皮:我们要做个官…

playbooks 分布式部署 LNMP

1、环境配置 ansible 服务器 192.168.10.10nginx 服务器 192.168.10.20mysql 服务器 192.168.10.21php 服务器 192.168.10.22 2、安装 ansble #192.168.10.10节点 yum install -y epel-release #先安装 epel 源 yum install -y ansible配置主机清单 …

计算机毕业设计-程序论文-基于web线上项目竞标平台的开发与实现

本系统开发采用技术为JSP、Bootstrap、Ajax、SSM、Java、Tomcat、Maven 此文章为本人亲自指导加编写,禁止任何人抄袭以及各类盈利性传播, 相关的代码部署论文ppt代码讲解答辩指导文件都有可私要 项目源码,请关注❥点赞收藏并私信博主&#x…