NestJS命名空间内用@Injectable导致Jest测试报错求助
解决NestJS命名空间内@Injectable装饰器导致Jest测试报错问题
问题描述
在NestJS项目中,将类通过TypeScript命名空间导出,并在命名空间内部的类上添加@Injectable()装饰器后,运行Jest测试时出现错误:
× Expected '{', got 'namespace'
移除@Injectable()装饰器后测试可正常运行,但业务需求要求保留该装饰器。
原因分析
TypeScript命名空间(namespace)与装饰器结合的语法,在ts-jest(Jest的TypeScript编译器)的解析逻辑中存在兼容性冲突。装饰器语法糖作用于命名空间内部类时,编译器的语法解析顺序或处理逻辑出现异常,导致报错。
解决方案
方案1:改用ES模块导出替代命名空间(推荐)
NestJS原生推荐使用ES模块规范,命名空间属于TypeScript旧语法,改用模块导出更适配Nest生态,同时避免语法兼容性问题。
修改业务代码:
// search-case.ts import { Injectable } from '@nestjs/common'; import { InterfaceRepository } from './your-repo-path'; import { Case } from './your-case-path'; import { RequestSearchCase, ResponseSearchCase } from './your-dto-path'; @Injectable() export class SearchCase { constructor(private casesRepository: InterfaceRepository<Case.Case>) { } async execute(request: RequestSearchCase): Promise<ResponseSearchCase> { const searchResult = await this.casesRepository.search(request.search); return { searchResult }; } } // 若需分组导出,可创建模块文件统一导出 // search-case-group.ts export * from './search-case'; // 或模拟命名空间结构(可选) export const SearchCase = { SearchCase: SearchCase };
测试代码调整引用:
import { SearchCase } from './search-case'; // 若用分组导出则改为: // import { SearchCase } from './search-case-group'; describe('Search Case', () => { it('should return a case matching the substring', async () => { const casesRepository = new InMemoryCaseRepository(); const searchCase = new SearchCase(casesRepository); const createCase = new Case.Case({ utente: 'utente test', caseOrigin: 'case origin test', reportingDate: new Date(), reporterName: 'Emanuela Xavier', disease: 'disease test', }); await casesRepository.create(createCase); const response = await searchCase.execute({ search: 'Ema' }); expect(response.searchResult.length).toBe(1); expect(response.searchResult[0].reporterName).toContain('Ema'); }); });
方案2:调整ts-jest配置兼容命名空间语法
若必须保留命名空间,可修改编译配置让ts-jest正确解析命名空间与装饰器的组合语法:
- 更新
tsconfig.json的编译选项:
{ "compilerOptions": { "target": "ES2020", "module": "CommonJS", "experimentalDecorators": true, "emitDecoratorMetadata": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "isolatedModules": false // 关闭单文件编译,确保支持命名空间跨文件解析 } }
- 修改
jest.config.js的ts-jest配置:
module.exports = { preset: 'ts-jest', testEnvironment: 'node', transform: { '^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.json', diagnostics: false // 关闭诊断日志,避免干扰 }] } };
方案3:手动调用装饰器函数(兜底方案)
绕过装饰器语法糖,直接调用Injectable装饰器函数作用于类,避免编译器解析异常:
import { Injectable } from '@nestjs/common'; export namespace SearchCase { export class SearchCase { constructor(private casesRepository: InterfaceRepository<Case.Case>) { } async execute(request: RequestSearchCase): Promise<ResponseSearchCase> { const searchResult = await this.casesRepository.search(request.search); return { searchResult }; } } // 手动应用Injectable装饰器 Injectable()(SearchCase); }
总结
优先选择方案1,ES模块更符合NestJS的设计理念,能减少后续维护中的兼容性问题;若业务强制要求保留命名空间,依次尝试方案2和方案3。
内容的提问来源于stack exchange,提问作者Evandro Silva
相关产品推荐
相关产品推荐

