NestJS构造函数类提供者依赖注入为undefined,令牌提供者正常
NestJS无提供者令牌依赖注入异常排查与解决
问题场景
尝试在NestJS中实现无需自定义提供者令牌的依赖注入,带令牌的配置可正常运行,但移除令牌后出现依赖注入失败的情况,且仅在Vitest+Esbuild环境下复现,使用TypeScript编译器时无异常。
正常工作的配置
使用自定义字符串令牌时,依赖注入正常:
@Injectable() export class AClass { constructor() { console.log('AClass'); } } @Injectable() export class BClass { constructor(@Inject('A') readonly a: AClass) { console.log('BClass', a); } } @Module({ providers: [ { provide: 'A', useClass: AClass }, { provide: 'B', useClass: BClass }, ], exports: ['A', 'B'], }) export class CModule {} const moduleRef = await Test.createTestingModule({ imports: [CModule], }) .setLogger( new ConsoleLogger('logger', { logLevels: ['log', 'error', 'warn', 'debug', 'verbose'], }), ) .compile(); console.log('about to get BClass'); const x = moduleRef.get('B'); console.log('x:', x);
移除令牌后的异常配置
移除自定义令牌,直接通过类类型注入时,BClass构造函数中的AClass参数变为undefined:
@Injectable() export class BClass { constructor(readonly a: AClass) { console.log('BClass', a); } } @Module({ providers: [AClass, BClass], exports: [AClass, BClass], }) export class CModule {} console.log('about to get BClass'); const x = moduleRef.get(BClass); console.log('x:', x);
异常日志表现
异常案例中类实例化时机早于NestJS核心日志,且依赖注入失败:
[Nest] 76049 - 04/20/2024, 4:41:37 AM LOG [InjectorLogger] Resolving dependency Reflector in the Reflector provider (alias) stdout | unknown test AClass BClass undefined [Nest] 76049 - 04/20/2024, 4:41:37 AM LOG [InjectorLogger] Looking for Reflector in InternalCoreModule
问题根源
NestJS的自动依赖注入(无需显式令牌)依赖TypeScript的emitDecoratorMetadata编译选项,该选项会在编译时将构造函数的参数类型元数据注入到装饰器中,供NestJS的反射系统解析。而Esbuild默认不支持emitDecoratorMetadata,导致NestJS无法识别BClass构造函数中AClass的参数类型,从而无法完成自动注入。
解决方案
方案1:启用Esbuild的装饰器元数据支持
在Vitest配置文件(vite.config.ts)中,为Esbuild添加装饰器元数据相关配置:
import { defineConfig } from 'vitest/config'; import nestjs from '@nestjs/vite-plugin'; export default defineConfig({ plugins: [nestjs()], test: { // 其他测试配置 }, esbuild: { tsconfigRaw: { compilerOptions: { emitDecoratorMetadata: true, experimentalDecorators: true, }, }, }, });
方案2:使用SWC替代Esbuild
SWC支持emitDecoratorMetadata,可以在Vitest中配置使用SWC:
- 安装依赖:
npm install @swc/core @swc/jest --save-dev
- 修改Vitest配置:
import { defineConfig } from 'vitest/config'; import nestjs from '@nestjs/vite-plugin'; export default defineConfig({ plugins: [nestjs()], test: { transformMode: { web: [/\.[jt]sx?$/], }, transform: { '^.+\\.[jt]sx?$': '@swc/jest', }, // 其他测试配置 }, });
- 在
swc.config.json中启用装饰器元数据:
{ "jsc": { "transform": { "decoratorMetadata": true, "decorators": true } } }
方案3:手动指定注入令牌
如果不想调整构建工具配置,可以回到显式注入的方式,但使用类本身作为令牌(而非自定义字符串):
@Injectable() export class BClass { constructor(@Inject(AClass) readonly a: AClass) { console.log('BClass', a); } }
内容的提问来源于stack exchange,提问作者okhobb
相关产品推荐
相关产品推荐

