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

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:

  1. 安装依赖:
npm install @swc/core @swc/jest --save-dev
  1. 修改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',
    },
    // 其他测试配置
  },
});
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:30:58