javascript - Typescript - 对装饰器的循环依赖

标签 javascript node.js typescript import circular-dependency

我对我的装饰器有一个循环依赖,因为我的类(class) ThingAThingB 有关系反之亦然。
我已经阅读了有关此问题的几个问题:

  • Beautiful fix for circular dependecies problem in Javascript / Typescript
  • TypeScript Decorators and Circular Dependencies

  • 但我无法为我的案例找到有效的解决方案。
    我尝试了很多人建议从 @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/

    相关文章:

    Javascript 切换可见性多个 div

    javascript - 脚本有效但不适用于 node.js

    angular - 捕获 typescript promise 中的错误

    node.js - 如何在另一个文件夹中使用我的主应用程序文件夹中的 Mongoose 模型?

    typescript - typescript 中的打字是什么?

    angular - 如何在 Angular 4 中使用 post 请求 API 发送多个文件

    javascript - Lookahead (?=模式) 没有前面的模式

    javascript - 同步调用(等待 ad.authenticate)

    javascript - 如何使用 mapbox 样式连接本地 JSON 数据

    javascript - 终端输出 : manpath: can't set the locale; make sure $LC_* and $LANG are correct