Typescript-デコレータへの循環依存
私のクラスThingAはと関係がThingBあり、その逆もあるため、デコレータに循環依存関係になります。この問題に関するいくつかの質問を読みました。
Javascript / Typescriptの循環依存問題の美しい修正
TypeScriptデコレータと循環依存
しかし、私は自分のケースに有効な解決策を見つけることができませんでした。多くの人々がから変更に提案として私が試し@hasOne(ThingA)に@hasOne(() => ThingA)遅延読み込みを強制し、依存関係を壊すために、私は、コンストラクタの名前を取得することはできませんだから、このソリューションは動作しません。コンストラクターThingBのメタデータに追加するには、コンストラクターの名前(例: 'ThingA')が必要です。
私の元のコードに従う(lazyloadの変更なし)
ThingA
@hasAtLeast(0, ThingB)
export class ThingA extends RTContent {
@IsEmail()
email: string;
}
ThingB
@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)
}
}
重要なのは、変数値へのアクセスを遅らせることです。
このハックが機能するためには、これらのデコレータは副作用のみであり、コンストラクタを変更しないという事実に依然依存しています。したがって、これは循環依存問題の一般的な解決策ではありません。より一般的なパターンは、コース外の遅延評価です。しかし、もっと複雑ですが、本当に必要な場合は、plsはコメントで尋ねます。
あなたの場合、上記のimplは機能するはずです。ただしThingA、モジュールのトップレベル内でインスタンス化またはBを実行してはなりませんsetTimeout。これは、コールバックの前に発生するため、ハックを中断します。