You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何export *与逐个导出在CommonJS下会导致Jest测试配置差异?

为什么export *在CommonJS模块下会导致Jest spyOn失败,而逐个导出不会?

问题场景

在使用Jest测试时遇到报错:

TypeError: Cannot redefine property: getCurrentRouteSnapshot

原本在index.ts中使用export * from './router.tools'批量导出成员,测试用例中无法用spyOn监听这些导出的函数;改成逐个明确导出后,测试恢复正常。对应的测试环境TypeScript配置(tsconfig.spec.json)中module被设置为CommonJS。

根本原因分析

问题核心在于TypeScript对两种导出方式编译为CommonJS代码时的行为差异:

1. export *的编译逻辑

当使用export *时,TypeScript会把目标模块(./router.tools)的整个导出对象直接合并到当前模块的exports上,编译后的JS代码类似:

const routerTools = require('./router.tools');
Object.assign(exports, routerTools);

由于CommonJS模块导出的属性默认是**不可配置(non-configurable)**的(Node.js模块系统的固有特性),通过Object.assign复制过来的属性会继承这个特性——也就是说,这些属性的configurable描述符为false。

而Jest的spyOn本质是通过修改属性的描述符(比如替换成带监听逻辑的getter/setter)来实现的,当属性不可配置时,就会抛出Cannot redefine property的错误。

同时,这种场景下TypeScript不会给当前模块的exports添加__esModule: true标记,进一步导致ES模块与CommonJS模块交互时的属性配置行为不符合预期。

2. 逐个导出的编译逻辑

当明确逐个导出成员时,TypeScript会为每个成员生成单独的赋值语句,编译后的JS代码类似:

const routerTools = require('./router.tools');
exports.getCurrentRouteSnapshot = routerTools.getCurrentRouteSnapshot;
exports.GetCurrentApplication = routerTools.GetCurrentApplication;
// ... 其他成员

这种直接赋值的方式添加到exports上的属性,默认是**可配置(configurable)**的——Jest的spyOn可以正常修改这些属性的描述符,因此不会报错。

另外,TypeScript会为这种导出方式的模块添加__esModule: true标记,确保ES模块的导入行为符合预期,同时也间接保证了导出属性的可配置性。

总结

  • export *在CommonJS编译目标下会复制不可配置的属性到当前模块导出,导致Jest无法修改属性实现监听;
  • 逐个导出会生成可配置的属性赋值,让Jest的spyOn可以正常工作。

内容的提问来源于stack exchange,提问作者IMeyers20

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 19:53:12