如何在NestJS服务中注入泛型?求技术指导
在NestJS中实现泛型BaseService的正确姿势
这个问题我之前也踩过坑,核心原因是TypeScript泛型是编译时类型,运行时会被擦除,Nest的依赖注入系统根本无法获取到Model的具体实体类型,所以直接用@InjectRepository(Model)是无效的。下面给你两种可行的实现方案:
方案一:子类注入具体Repository并传递给父类
这是最直观、最常用的方式,子类负责注入自己对应的Repository,然后通过构造函数传给父类的泛型BaseService:
// base.service.ts import { Injectable } from "@nestjs/common"; import { FindManyOptions, Repository } from "typeorm"; // 用@Injectable()代替@Component(),Nest中服务类推荐用这个装饰器 @Injectable() export class BaseService<Model> { constructor(protected readonly modelRepository: Repository<Model>) {} // 通用查询方法 public async find(options?: FindManyOptions<Model>): Promise<Model[]> { return this.modelRepository.find(options); } // 可以继续添加其他通用方法,比如findOne、create、update、delete等 public async findOne(id: number): Promise<Model | null> { return this.modelRepository.findOneBy({ id } as any); } }
然后在具体的业务服务中继承BaseService,并注入对应的实体Repository:
// user.service.ts import { Injectable } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; import { Repository } from "typeorm"; import { User } from "./user.entity"; import { BaseService } from "../base/base.service"; @Injectable() export class UserService extends BaseService<User> { constructor( // 注入User实体对应的Repository @InjectRepository(User) private readonly userRepository: Repository<User>, ) { // 把具体的Repository传给父类构造函数 super(userRepository); } // 这里可以添加UserService特有的业务方法 public async findByEmail(email: string): Promise<User | null> { return this.userRepository.findOne({ where: { email } }); } }
别忘了在对应的Module中注册实体:
// user.module.ts import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "./user.entity"; import { UserService } from "./user.service"; @Module({ imports: [TypeOrmModule.forFeature([User])], // 注册User实体,让DI能找到对应的Repository providers: [UserService], exports: [UserService], }) export class UserModule {}
方案二:通过EntityManager动态获取Repository
如果不想每个子类都写构造函数传递Repository,可以在BaseService中注入EntityManager,让子类传入实体类,父类动态获取对应的Repository:
// base.service.ts import { Injectable, Inject } from "@nestjs/common"; import { FindManyOptions, Repository, EntityManager, ObjectLiteral } from "typeorm"; @Injectable() // 添加泛型约束,确保Model是TypeORM实体类型 export class BaseService<Model extends ObjectLiteral> { protected readonly modelRepository: Repository<Model>; constructor( @Inject(EntityManager) private readonly entityManager: EntityManager, // 接收实体类的构造函数,用于运行时获取Repository private readonly entityClass: new () => Model, ) { // 动态获取对应实体的Repository this.modelRepository = this.entityManager.getRepository(entityClass); } // 通用方法示例 public async find(options?: FindManyOptions<Model>): Promise<Model[]> { return this.modelRepository.find(options); } }
子类实现更简洁,只需要传入实体类即可:
// user.service.ts import { Injectable } from "@nestjs/common"; import { User } from "./user.entity"; import { BaseService } from "../base/base.service"; @Injectable() export class UserService extends BaseService<User> { constructor() { // 直接传入User实体类的构造函数 super(User); } // 特有方法 public async findByEmail(email: string): Promise<User | null> { return this.modelRepository.findOne({ where: { email } }); } }
关键注意点
- 替换
@Component()为@Injectable():NestJS中服务类标准装饰器是@Injectable(),@Component()更多用于传统Angular风格的组件,虽然两者都能注册为提供者,但推荐遵循Nest的规范。 - 实体必须注册:所有用到的实体都要在对应的Module中通过
TypeOrmModule.forFeature([Entity])注册,否则TypeORM无法创建对应的Repository实例。 - 泛型约束:添加
Model extends ObjectLiteral可以确保传入的泛型是合法的TypeORM实体类型,避免类型错误。
内容的提问来源于stack exchange,提问作者Adis Sun
相关产品推荐
相关产品推荐

