解决TypeORM循环依赖

使用nestjs编写后端服务,免不了要访问数据库,一般我们是使用TypeORM来访问

1. 问题长什么样?

Entity A 引用 Entity B,Entity B 又引用 Entity A → 编译/运行时爆炸

ReferenceError: Cannot access 'User' before initialization

2. 解决思路一句话

“把类留给自己,把名字留给对方”

  • 自己内部用 import type 拿类型
  • 对方用 字符串延迟箭头 去解析,不形成顶层 import 环

3. 最小可运行示例

目录结构

src/
 ├─ entity/
 │   ├─ user.ts
 │   └─ post.ts
 └─ index.ts

3.1 安装依赖

1
2
3
npm i typeorm reflect-metadata sqlite3
npm i -D typescript ts-node
npx tsc --init

3.2 entity/user.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import type { Post } from './post';   // ① 只拿类型,不拿实现

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column()
  name!: string;

  // ② 字符串 + 泛型:TypeORM 运行时用 'Post',TS 编译时用 Post 类型
  @OneToMany<Post>('Post', p => p.author)
  posts!: Post[];
}

3.3 entity/post.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import type { User } from './user';

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column()
  title!: string;

  // ③ 同样套路
  @ManyToOne<User>('User', u => u.posts, { onDelete: 'CASCADE' })
  author!: User;
}

3.4 启动文件 index.ts

 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
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { User } from './entity/user';
import { Post } from './entity/post';

const AppDataSource = new DataSource({
  type: 'sqlite',
  database: ':memory:',
  entities: [User, Post],
  synchronize: true,
  logging: false,
});

async function main() {
  await AppDataSource.initialize();

  const user = await AppDataSource.manager.save(User, { name: 'Alice' });
  await AppDataSource.manager.save(Post, { title: 'Hello', author: user });

  const posts = await AppDataSource.manager.find(Post, { relations: ['author'] });
  console.log(posts);   // 成功打出关联数据
  await AppDataSource.destroy();
}

main();

4. 运行验证

1
2
npx ts-node src/index.ts
# 输出: [ Post { id: 1, title: 'Hello', author: User { id: 1, name: 'Alice' } } ]

无报错,循环依赖已消失。

5. 口诀总结

场景写法
自己要用对方类型import type { Other } from './other'
装饰器里引用对方@OneToMany<Other>('Other', o => o.xxx)@ManyToOne(() => Other, …)
绝不出现import { Other } from './other' 同时又被对方 import

记住 “类型归 TS,名字归 ORM”,循环依赖永远说再见。

updatedupdated2025-10-142025-10-14
加载评论