且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

打字稿-对装饰器的循环依赖

更新时间:2022-12-23 11:52:43

我看到您的装饰器实际上并没有修改构造函数,它只是运行一些副作用代码来添加一些元数据条目。因此, @decorator 语法不是必须的。

I see that your decorator doesn't actually modify the constructor, it merely runs some side-effect code to add some metadata entry. Thus the @decorator syntax isn't a must.

我的建议是您不要装饰 ThingA ThingB ,只需按原样导出它们即可。您将装饰推迟到另一个模块中,该模块应该是 ThingA ThingB 的共同父对象。这样可以解决循环依赖性。

My advice is that you decorate neither ThingA nor ThingB, just export them as-is. You defer the decoration in another module, which should be the common parent of both ThingA and ThingB. This way circular dependency is resolved.

例如,在'./ things / index.ts'中,您执行以下操作:

For example, in './things/index.ts' you do:

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(或B).ts'。这样可以确保在实例化类之前执行装饰。

Now other part of your code can import from './things/index.ts', instead of directly from './things/ThingA(or B).ts'. This would ensure the decoration is executed before instantiation of classes.

如果必须使用装饰器,那么***的选择是惰性加载。 @hasOne(()=> ThingA)应该可以解决问题,但是您需要修改 hasOne 的实现

If you must use decorator, well lazy load is your best bet. @hasOne(() => ThingA) should does the trick, but you need to modify the implementation of hasOne accordingly, a little hack.

function hasOne(target) {
  if (typeof target === "function") {
    setTimeout(() => {
      _workOnTarget(target())
    }, 0)
  } else {
    _workOnTarget(target)
  }
}

关键是要延迟访问变量值。

要使此hack起作用,我们仍然依赖这些装饰器仅具有副作用的事实,不要修改构造器。因此,这不是循环依赖问题的一般解决方案。更一般的模式是偏离路线的懒惰评估。不过,如果您确实需要,则更复杂些,请在评论中提问。

For this hack to work, we still rely on the fact that these decorators are side-effect only, don’t modify the constructor. So this is NOT a general solution to circular dependency problem. More general pattern is off course lazy evaluation. More complicated though, if you really need, pls ask in comments.

对于您的情况,上述impl应该有效。但是您不能在任何模块的顶层内部实例化 ThingA 或B,因为在 setTimeout 回调之前会发生cuz,因此破解黑客。

For your case, above impl should work. But you must not instantiate ThingA or B right inside any module’s top level, cuz that would happen before setTimeout callback, thus break the hack.