当前位置: 首页 > news >正文

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模块系统)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Redis7-入门-安装
  • C#使用csvhelper实现csv的操作
  • 【数据采集与可视化案例】基于python的国家级非物质文化遗产数据采集与可视化分析
  • 【漏洞扫描器】使用nessus扫描工具扫描服务器,并生成漏扫报告
  • SpringBoot Vue用自签名证书SSL配置https,http转发到https(整理文章)
  • test1111
  • 【前端】fis框架学习
  • 大模型是如何“炼”成的?揭秘AI背后的训练秘籍!
  • 消息推送只会用websocket、轮询?试试SSE,轻松高效。
  • Linux云计算 |【第二阶段】AUTOMATION-DAY5
  • moment.js的使用方法
  • mysql数据库知识总结
  • 10个append()函数在Python程序开发中的创新应用
  • 如何搭建数字人直播系统?快速上手方法来了!
  • docker安装phpMyAdmin
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • Babel配置的不完全指南
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • crontab执行失败的多种原因
  • Docker下部署自己的LNMP工作环境
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • Java多态
  • Koa2 之文件上传下载
  • Mybatis初体验
  • PHP那些事儿
  • python学习笔记 - ThreadLocal
  • scala基础语法(二)
  • underscore源码剖析之整体架构
  • 翻译--Thinking in React
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 关于Java中分层中遇到的一些问题
  • 马上搞懂 GeoJSON
  • 前嗅ForeSpider教程:创建模板
  • 区块链将重新定义世界
  • 深度学习在携程攻略社区的应用
  • 微信小程序:实现悬浮返回和分享按钮
  • 小试R空间处理新库sf
  • 写代码的正确姿势
  • 责任链模式的两种实现
  • 回归生活:清理微信公众号
  • 如何在招聘中考核.NET架构师
  • (C语言)字符分类函数
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (八)c52学习之旅-中断实验
  • (二)hibernate配置管理
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (九)信息融合方式简介
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (十三)MipMap
  • (四)stm32之通信协议
  • (转)scrum常见工具列表
  • (转)创业的注意事项