我对装饰器有循环依赖关系,因为我的类ThingA
与ThingB
有关系,反之亦然。
我已经阅读了有关此问题的几个问题:
但是我无法为我的案件找到有效的解决方案。
我尝试了很多人建议从@hasOne(ThingA)
更改为@hasOne(() => ThingA)
来强制延迟加载并破坏依赖关系,但是该解决方案不起作用,因为我无法获得构造函数名称。
我需要构造函数的名称(例如:“ ThingA”)将其添加到构造函数ThingB的元数据中。
以下是我的原始代码(未修改lazyload)
ThingA
@hasAtLeast(0, ThingB)
export class ThingA extends RTContent {
@IsEmail()
email: string;
}
事物B
@hasOne(ThingA)
export class ThingB extends RTContent {
@IsString()
description: string;
}
装饰器:
export type LinkConstraint<C extends RTContent> = {
content: string; // name of the constructor (ex. 'ThingA')
maxOccurrences: number;
minOccurrences: number;
constructor: { new(...args: any[]): C };
}
function constraintFactory<C extends RTContent>(minOccurrences: number, maxOccurrences: number, associated: { new(...args: any[]): C }) {
return (constructor: Function) => {
const constraints = Reflect.getMetadata('linkConstraints', constructor) || [];
const constraint: LinkConstraint<C> = {
content: associated?.name,
minOccurrences,
maxOccurrences,
constructor: associated
};
constraints.push(constraint);
Reflect.defineMetadata('linkConstraints', constraints, constructor)
}
}
export function hasOne<C extends RTContent>(associated: { new(...args: any[]): C }) {
return constraintFactory(1, 1, associated)
}
export function hasAtLeast<C extends RTContent>(minOccurrences: number, associated: { new(...args: any[]): C }) {
return constraintFactory(minOccurrences, Infinity, associated)
}
答案 0 :(得分:2)
我看到您的装饰器实际上并没有修改构造函数,它只是运行一些副作用代码来添加一些元数据条目。因此,@decorator
语法不是必须的。
我的建议是,您既不装饰ThingA
也不装饰ThingB
,只需按原样导出即可。您将装饰推迟到另一个模块中,该模块应该是ThingA
和ThingB
的共同父对象。这样可以解决循环依赖性。
例如,您在'./things/index.ts'
中执行
import { ThingA } from './things/ThingA';
import { ThingB } from './things/ThingB';
hasOne(ThingA)(ThingB);
hasAtLeast(0, ThingB)(ThingA);
export { ThingA, ThingB }
现在,您代码的其他部分可以从'./things/index.ts'
导入,而不是直接从'./things/ThingA(or B).ts'
导入。这样可以确保在实例化类之前执行装饰。
如果必须使用装饰器,那么最好是延迟加载。 @hasOne(() => ThingA)
应该可以解决问题,但是您需要相应地修改hasOne
的实现,有点麻烦。
function hasOne(target) {
if (typeof target === "function") {
setTimeout(() => {
_workOnTarget(target())
}, 0)
} else {
_workOnTarget(target)
}
}
关键是要延迟访问变量值。
要使此技巧正常工作,我们仍然依靠以下事实:这些装饰器仅具有副作用,请不要修改构造器。因此,这不是循环依赖问题的一般解决方案。更一般的模式是偏离路线的懒惰评估。不过,如果您确实需要更复杂,请在评论中提问。
对于您的情况,上面的impl应该有效。但是您不能在任何模块的顶层内部实例化ThingA
或B,因为在setTimeout
回调之前可能会发生cuz,从而破坏了破解。