typescript - การพึ่งพาแบบวงกลมกับมัณฑนากร

Sep 18 2020

ฉันได้รับการพึ่งพาแบบวงกลมกับมัณฑนากรของฉันเนื่องจากชั้นเรียนของฉันThingAมีความสัมพันธ์ThingBและในทางกลับกัน ฉันได้อ่านคำถามหลายข้อเกี่ยวกับปัญหานี้:

  • การแก้ไขที่สวยงามสำหรับปัญหาการพึ่งพาแบบวงกลมใน Javascript / typescript

  • TypeScript Decorators และ Circular Dependencies

แต่ฉันไม่พบวิธีแก้ปัญหาที่ถูกต้องสำหรับกรณีของฉัน ฉันพยายามตามที่หลายคนแนะนำให้เปลี่ยนจาก@hasOne(ThingA)เป็น@hasOne(() => ThingA)บังคับให้โหลดขี้เกียจและทำลายการพึ่งพา แต่วิธีนี้ใช้ไม่ได้ผลเพราะฉันไม่สามารถรับชื่อตัวสร้าง ฉันต้องการชื่อของตัวสร้าง (ตัวอย่าง: 'ThingA') เพื่อเพิ่มในข้อมูลเมตาของตัวสร้าง ThingB

ทำตามรหัสเดิมของฉัน (โดยไม่ต้องแก้ไข lazyload)

สิ่ง

@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)
}

คำตอบ

2 hackape Sep 26 2020 at 14:26

ฉันเห็นว่ามัณฑนากรของคุณไม่ได้แก้ไขตัวสร้างจริงๆมันเป็นเพียงการเรียกใช้โค้ดผลข้างเคียงบางอย่างเพื่อเพิ่มรายการข้อมูลเมตา ดังนั้น@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)
  }
}

กุญแจสำคัญคือการชะลอการเข้าถึงค่าตัวแปร

เพื่อให้แฮ็คนี้ใช้งานได้เรายังคงอาศัยข้อเท็จจริงที่ว่านักตกแต่งเหล่านี้เป็นผลข้างเคียงเท่านั้นอย่าแก้ไขตัวสร้าง ดังนั้นนี่ไม่ใช่วิธีแก้ปัญหาทั่วไปสำหรับปัญหาการพึ่งพาแบบวงกลม รูปแบบทั่วไปมากขึ้นคือการประเมินแบบขี้เกียจนอกหลักสูตร ซับซ้อนกว่านี้ถ้าคุณต้องการจริงๆกรุณาถามในความคิดเห็น

สำหรับกรณีของคุณ im ด้านบนควรใช้งานได้ แต่คุณต้องไม่สร้างอินสแตนซ์ThingAหรือ B ภายในระดับบนสุดของโมดูลใด ๆ เพราะจะเกิดขึ้นก่อนที่จะsetTimeoutโทรกลับจึงทำให้แฮ็คพังได้