我对我的装饰器有一个循环依赖,因为我的类(class) ThingA
与 ThingB
有关系反之亦然。
我已经阅读了有关此问题的几个问题:
但我无法为我的案例找到有效的解决方案。
我尝试了很多人建议从
@hasOne(ThingA)
更改至@hasOne(() => ThingA)
强制延迟加载并打破依赖关系,但此解决方案不起作用,因为我无法获取构造函数名称。我需要构造函数的名称(例如:'ThingA')将其添加到构造函数 ThingB 的元数据中。
按照我的原始代码(没有延迟加载修改)
东西A
@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)
}
最佳答案
我看到您的装饰器实际上并没有修改构造函数,它只是运行一些副作用代码来添加一些元数据条目。因此 @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)
}
}
关键是延迟 访问变量值。为了让这个 hack 起作用,我们仍然依赖于这些装饰器只是副作用的事实,不要修改构造函数。所以这不是循环依赖问题的一般解决方案。更一般的模式是惰性评估。不过更复杂的是,如果你真的需要,请在评论中询问。
对于您的情况,上面的 impl 应该可以工作。但是你不能实例化
ThingA
或 B 位于任何模块的顶层,因为它会在 setTimeout
之前发生回调,从而打破黑客。
关于javascript - Typescript - 对装饰器的循环依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63953101/