TypeORM ESM 项目关系属性如何用 Relation 包装类型避免循环依赖
TypeORM ESM 项目关系属性如何用 Relation 包装类型避免循环依赖【免费下载链接】typeormTypeScript JavaScript ORM for Node.js — supports PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle, and more.项目地址: https://gitcode.com/GitHub_Trending/ty/typeorm在 TypeScript ESM 项目中使用 TypeORM 时两个实体类如果建立双向关系往往会互相 import 对方。此时反射机制保存的属性类型元数据会进入编译后的代码造成模块间的循环依赖导致实体加载失败。TypeORM 的文档给出的解决方式是在关系属性上改用RelationT包装类型来声明类型让编译产物中不再保存这份类型元数据。本文适用前提是你的项目使用 ESMpackage.json中含type: module且实体之间存在互相引用的关系定义。问题背景ESM 项目中反射元数据为什么会引发循环依赖TypeORM 的 FAQ 明确了 ESM 项目的一个前提package.json中需要添加type: module这样 TypeORM 才知道要对文件使用import( ... )加载见 docs/docs/help/1-faq.md。问题出在关系属性的类型标注上。TypeScript 装饰器编译后反射元数据会把属性标注的类型一并保存进产物代码。当Photo和PhotoMetadata这类双向关系的实体互相 import 时两边都会生成对对方的类型引用模块加载顺序无法确定就出现了文档所说的 circular dependency import issues。FAQ 同时说明了为什么可以用包装类型去掉这份元数据关系列的类型已经由OneToOne、ManyToOne等装饰器定义好了TypeScript 额外保存的那份属性类型元数据实际上没有被使用。解决方法在关系属性上使用 Relation 包装类型Relation从typeorm直接导入。它的定义在 src/common/RelationType.tsexport type RelationT T源码注释说明这是一个关系类型定义的包装类型用于绕过反射元数据保存属性类型所造成的 ESM 模块循环依赖问题并通过 src/index.ts 对外导出。也就是说RelationPhoto在类型层面完全等价于Photo唯一的作用是让编译产物不再保留该属性的具体类型。修改方式只有一处把关系属性的类型从实体类改为Relation实体类装饰器和其他写法不变。以 docs/docs/getting-started.md 中“Relations in ESM projects”一节的 Photo 与 PhotoMetadata 双向OneToOne关系为例修改后的实体如下import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn, Relation, } from typeorm import { Photo } from ./Photo Entity() export class PhotoMetadata { /* ... other columns */ OneToOne(() Photo, (photo) photo.metadata) JoinColumn() photo: RelationPhoto }import { Entity, Column, PrimaryGeneratedColumn, OneToOne, Relation, } from typeorm import { PhotoMetadata } from ./PhotoMetadata Entity() export class Photo { /* ... other columns */ OneToOne(() PhotoMetadata, (photoMetadata) photoMetadata.photo) metadata: RelationPhotoMetadata }注意两点反向属性通过(photo) photo.metadata这样的函数指定文档也说明可以改为直接传字符串metadata函数写法只是为了便于重构。JoinColumn只放在关系的一侧放了它的一侧就是关系拥有方owning side数据库外键列在这一侧的表中。限制不要用在非关系列上FAQ 中有明确提示Important: Do not useRelationon non-relation column typesRelation只用于带关系装饰器的属性OneToOne、ManyToOne、OneToMany等。普通Column()列不需要、也不应该加这个包装类型因为普通列的类型元数据仍然有用。替代方案用 import type 避免循环导入如果你不想在关系属性上使用Relationdocs/docs/relations/6-relations-faq.md 的 “Avoid circular import errors” 一节给出了另一条路径用import type导入对方实体的类型并在装饰器中用实体名字符串如Person而不是引用函数来声明关系。import type只在编译期保留类型信息不会生成实际的 JavaScript import 语句因此不会形成运行时模块循环。该文档以多对多关系为例展示了Action.ts与Person.ts互相引用的完整写法可参考原文。如何确认修改生效文档没有给出固定的报错文本或成功日志。判断依据是文档描述的两种状态修改前两个实体互相 import 的 ESM 项目会出现 circular dependency import issues改用RelationT后编译产物中不再保存关系属性的类型元数据该问题被消除修改后双向关系仍然正常工作——OneToOne等装饰器继续提供列类型关系加载功能不受影响。可以按 getting-started 中紧接着的 “Loading objects with their relations” 一节用find*方法或QueryBuilder在一条查询中加载实体及其关联对象验证关系定义依然有效。如果你的实体只存在单向关系、没有互相 import就不存在这个问题无需引入Relation包装类型。【免费下载链接】typeormTypeScript JavaScript ORM for Node.js — supports PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle, and more.项目地址: https://gitcode.com/GitHub_Trending/ty/typeorm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考