ts-jest运行测试报Class extends value undefined不是构造函数如何解决
问题根因
业务项目能正常运行但ts-jest跑测试报错,本质是两者的模块加载逻辑不一致:
- ts-node默认走ESM或适配了Node.js的循环依赖处理逻辑,导出提升逻辑更宽松,循环依赖场景下能拿到正确的父类引用
- ts-jest默认用CommonJS规范加载模块,循环依赖时加载顺序错乱,会出现父类还没完成导出就被引用,拿到
undefined导致继承报错
注意:该错误是JS运行时抛出的原生类型错误,不是Jest主动拦截的错误,没有开关可以直接关闭报错,只能通过调整加载逻辑绕开。
临时兼容方案(无需大规模重构循环依赖)
下面的方案按改造成本从低到高排序:
方案1:切换ts-jest为ESM加载模式
直接对齐ts-node的加载逻辑,是改造成本最低的方案,只需要修改配置文件:
- 修改
jest.config.js配置:
/** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { preset: 'ts-jest/presets/default-esm', extensionsToTreatAsEsm: ['.ts'], testEnvironment: 'node', transform: { '^.+\\.tsx?$': ['ts-jest', { useESM: true, }], }, // 如果有别名配置,需要补全后缀匹配规则 moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, }
- 确认
tsconfig.json中compilerOptions.module配置为NodeNext/ESNext,moduleResolution设置为NodeNext即可。
方案2:预加载循环依赖根模块
在jest的启动配置中提前加载所有循环依赖链的公共根模块,避免后续引用时未完成导出:
- 在项目根目录新建
jest.setup.ts,提前引入所有报错涉及的类所属的父文件:
// 示例:提前引入基类所在文件,保证先完成导出 import './src/base/BaseClass' import './src/common/CommonService'
- 在
jest.config.js中添加配置:
module.exports = { // 其他配置不变 setupFiles: ['<rootDir>/jest.setup.ts'], }
方案3:局部调整循环依赖文件的导出顺序
针对单个报错的文件,微调导入导出顺序适配CommonJS的加载逻辑:
// 原代码(会报错) import { Base } from './base' export class Child extends Base {} // 调整后代码 // 先占位导出 export let Child; // 再导入依赖 import { Base } from './base' // 实现类后赋值给导出变量 class ChildImpl extends Base {} Child = ChildImpl;
长期优化建议
后续迭代过程中建议逐步拆分循环依赖的模块,从根本上避免不同运行环境的加载逻辑差异问题。
内容的提问来源于stack exchange,提问作者StanFlint
相关产品推荐
相关产品推荐

