0%

nestjs 类型导出引起的循环依赖问题

nestjs 无法注入服务

问题发现

  • 在 viewService 里面导入 UserService,发现一直提示错误,没有找到可以注入的内容
  • 检查了一遍代码,确认逻辑代码没有问题
  • 怀疑是不是循环依赖了,检查代码,发现代码基本与其他 Moduel 的服务相同,没有出现循环依赖的情况
  • 打印 ModuleContainer,在当前 ViewModule 里面找到了导入的 UserModule,也存在 UserService

view.service.ts 尝试打印是否成功导入 UserModule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Injectable()
export class ViewService {
constructor(
private modulesContainer: ModulesContainer,
private userService: UserService,
@InjectRepository(ViewEntity)
private viewEntityRepository: Repository<ViewEntity>
) {
[...modulesContainer.values()].map((module) => {
if (module.metatype.name === "ViewModule") {
console.log("=======imports=======");
console.log(module.imports);
console.log("=======providers======");
console.log(module.providers);
console.log("=======exports======");
console.log(module.exports);

console.log("\n\n\n\n\n");
console.log("=======UserModule exports======");
[...module.imports.values()].map((innerModule) => {
if (innerModule.metatype.name === "UserModule") {
console.log(innerModule.exports);
}
});
}
});
}
}

view.module.ts

1
2
3
4
5
6
7
@Module({
imports: [TypeOrmModule.forFeature([ViewEntity, ViewLikeStatEntity]), UserModule],
controllers: [ViewController],
providers: [ViewService],
exports: [ViewService],
})
export class ViewModule {}

view.service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export type ViewRelationType = "topic";
export interface JoinListInterface {
uid: string;
avatar: string;
nickname: string;
roler: number;
}
@Injectable()
export class ViewService {
constructor(private readonly userService: UserService, @InjectRepository(ViewEntity) private readonly viewRepository: Repository<ViewEntity>) {}

create(createViewDto: CreateViewDto) {
return "This action adds a new view";
}
...
}

控制台

1
2
3
4
5
6
7
8
[error] 2021-07-28 18:11:32.1 [ExceptionHandler] Nest can't resolve dependencies of the ViewService (?, ViewEntityRepository). Please make sure that the argument dependency at index [0] is available in the ViewModule context.

Potential solutions:
- If dependency is a provider, is it part of the current ViewModule?
- If dependency is exported from a separate @Module, is that module imported within ViewModule?
@Module({
imports: [ /* the Module containing dependency */ ]
})

解决问题

  • 再次检查代码,发现 view.service.ts 导出了三个东西,一个 type,一个 interface,还有一个 viewService 服务
  • 全文查找导出的接口,发现在一个实体里面有这个类型的引入,就是 JoinListInterface.

topic.entity.ts

1
2
3
4
5
6
7
8
9
10
@Entity("topics", { schema: "42how" })
export class TopicEntity {
@PrimaryGeneratedColumn({ type: "bigint", name: "id", unsigned: true })
id: number;

join: {
count?: number;
list?: JoinListInterface[];
} = { count: 0, list: [] };
}
  • 尝试删除 JoinListInterface ,服务注入成功

  • 原本以为 ts 的 interface,type 是不参与到编译过后的 js 文件,检查编译后 topic.entity.js 文件,发现 const view_service_1 = require("../modules/view/view.service"); 这一句,虽然 interface 没有被使用,但是这个 js 文件被引入了。因为这个实体类被引入的比较多,产生了循环依赖,在 nestjs 进行注入的时候,这个文件导出是 undefined,查找不到服务,无法注入。

总结

  • ts 中 export 的类型文件应该单独定义,不要跟逻辑代码放在一个文件。
  • 类型文件虽然没有参与编译,但是会引入文件,如果引用的过于复杂,会导致循环依赖的情况
  • 感谢青木大佬的帮助