Typescript - dekoratöre döngüsel bağımlılık
Dekoratörlerime döngüsel bir bağımlılık yaşıyorum çünkü sınıfımın ThingAbir ilişkisi var ThingBve bunun tersi de geçerli. Bu sorunla ilgili birkaç soru okudum:
Javascript / Typescript'te döngüsel bağımlılıklar sorunu için güzel düzeltme
TypeScript Dekoratörleri ve Dairesel Bağımlılıklar
Ancak davam için geçerli bir çözüm bulamadım. Birçok kişinin önerdiği kadar çok kişinin @hasOne(ThingA)yerine @hasOne(() => ThingA)geçerek tembel yüklemeyi zorlamayı ve bağımlılığı kırmayı denedim , ancak bu çözüm çalışmıyor çünkü yapıcı adını alamıyorum. Yapıcı ThingB'nin meta verilerine eklemek için kurucunun adına ihtiyacım var (örnek: 'ThingA').
Orijinal kodumun ardından (lazyload değişikliği olmadan)
Şey
@hasAtLeast(0, ThingB)
export class ThingA extends RTContent {
@IsEmail()
email: string;
}
ThingB
@hasOne(ThingA)
export class ThingB extends RTContent {
@IsString()
description: string;
}
Dekoratörler:
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)
}
Yanıtlar
Dekoratörünüzün yapıcıyı gerçekten değiştirmediğini görüyorum, sadece bazı meta veri girişi eklemek için bazı yan etki kodları çalıştırıyor. Bu nedenle @decoratorsözdizimi bir zorunluluk değildir.
Benim tavsiyem, ne dekore edersiniz ne ThingAde ThingBolduğu gibi ihraç edersiniz . Her iki ortak ebeveyn olmalıdır başka modülde dekorasyon, erteleme ThingAve ThingB. Bu şekilde döngüsel bağımlılık çözülür.
Örneğin, './things/index.ts'şunları yaparsınız:
import { ThingA } from './things/ThingA';
import { ThingB } from './things/ThingB';
hasOne(ThingA)(ThingB);
hasAtLeast(0, ThingB)(ThingA);
export { ThingA, ThingB }
Artık kodunuzun diğer kısmı './things/index.ts'doğrudan kaynak yerine şuradan içe aktarılabilir './things/ThingA(or B).ts'. Bu, dekorasyonun sınıfların somutlaştırılmasından önce yürütülmesini sağlayacaktır.
Dekoratör kullanmanız gerekiyorsa, tembel yük sizin en iyi seçeneğinizdir. @hasOne(() => ThingA)hile yapmalı, ancak uygulamayı hasOnebuna göre küçük bir hacklemeniz gerekiyor.
function hasOne(target) {
if (typeof target === "function") {
setTimeout(() => {
_workOnTarget(target())
}, 0)
} else {
_workOnTarget(target)
}
}
Anahtar, değişken değerine erişimi geciktirmektir .
Bu hacklemenin işe yaraması için, bu dekoratörlerin yalnızca yan etki olduğu gerçeğine güveniyoruz, yapıcıyı değiştirmeyin. Dolayısıyla bu, döngüsel bağımlılık sorununa genel bir çözüm DEĞİLDİR. Daha genel bir model elbette tembel değerlendirmedir. Daha karmaşık olsa da, gerçekten ihtiyacınız varsa, lütfen yorumlarda sorun.
Durumunuz için yukarıdaki impl çalışmalıdır. Ancak, ThingAherhangi bir modülün en üst seviyesinin içinde somutlaştırmamalı veya B yapmamalısınız , çünkü bu geri aramadan önce gerçekleşecek setTimeoutve böylece hacklemeyi kırmalısınız.