NestJS中使用抽象类作为Provider令牌无法解析依赖的问题
问题
在NestJS中,我在Module A中提供并导出如下Provider:
const userRepositoryProvider = { provide: UserRepository, // abstract class useClass: ConcreteUserRepository, // concrete class };
所有类均已标记为@Injectable。在Module B中导入Module A后,在Module B的服务中注入UserRepository:
private readonly userRepo: UserRepository
构建运行应用时,Nest无法解析该依赖,提示UserRepository为"?"。
但将Provider的provide令牌改为字符串"UserRepository",并通过@Inject("UserRepository")注入时:
const userRepositoryProvider = { provide: "UserRepository", useClass: ConcreteUserRepository, };
@Inject("UserRepository") private readonly userRepo: UserRepository
则可正常运行无异常。
相关抽象类与实现类代码如下:
@Injectable() export abstract class UserRepository { getUser(id: bigint): User { return User.create(id); } } @Injectable() export class ConcreteUserRepository extends UserRepository {}
请问为何会出现这种差异?是否属于Bug?
原因分析与解答
这不是NestJS的Bug,核心原因是TypeScript类型在编译后会被擦除,导致Nest的依赖注入系统无法通过类型令牌找到对应的Provider。
- 当你用
UserRepository(抽象类)作为provide令牌时,TypeScript编译成JavaScript后,这个抽象类的类型信息会丢失。Nest在运行时只能拿到一个空的类引用,无法正确关联到你定义的Provider,所以会提示无法解析依赖。 - 改用字符串令牌
"UserRepository"时,字符串在编译后会被保留,Nest可以直接通过这个明确的字符串标识找到对应的Provider,因此注入正常。
另外需要注意:虽然你给抽象类加了@Injectable(),但抽象类本身不能被实例化,Nest的DI系统需要明确的运行时标识来关联实现类。当使用类作为令牌时,Nest默认会尝试实例化该类,但抽象类做不到,这也是导致解析失败的一个辅助因素——即使类型信息没被擦除,Nest也无法实例化抽象类,而你通过useClass指定了实现类,但编译后的类型擦除让Nest找不到这个关联关系。
正确的实现方式有两种:
- 继续使用字符串或Symbol作为令牌,配合
@Inject()装饰器完成注入 - 利用Nest的自定义令牌机制,确保运行时存在可被识别的唯一标识,避免依赖TypeScript的类型信息
内容的提问来源于stack exchange,提问作者JH Lee
相关产品推荐
相关产品推荐

