ecmascript 6 - TypeScript-arv och cirkulära beroenden i SystemJS


Jag använder TypeScript med --module system (SystemJS) i ett mycket stort projekt. SystemJS stöder cykliska beroenden, och oftast fungerar det bra. Men när TypeScript-arv kommer in i bilden börjar saker och ting gå sönder.

Till exempel, om en klass A är beroende av klass B, och klass B ärver från klass A, då om klass A laddas först:

  1. Det kommer att pausa klass A:s upplösning och kommer att försöka ladda klass B-beroendet
  2. class B kommer att tro att dess beroenden är lösta, eftersom class A har blivit berörd.
  3. klass B:s arv kommer att misslyckas med att lösas, eftersom klass A fortfarande är odefinierad.

De flesta av de "lösningar" jag kan hitta på nätet för cirkulära beroenden med modulhanterare är antingen:

  • Ändra din design/kombinera klasser till en ensam modul
  • CommonJS och icke-TypeScript-specifika lösningar

Jag känner att det finns giltiga skäl för cirkulära konstruktioner, och att kombinera klasser till gigantiska filer är inte alltid önskvärt, så vänligen betrakta dessa lösningar som irrelevanta för den fråga jag ställer.

Finns det några lösningar på det faktiska problemet?

Svar #1

Att ändra din design är den mest fördelaktiga lösningen. En klass bör inte bero på sina underklasser. Om du använder dem i en fabrik eller liknande, är det en separat fråga och bör placeras i en separat klass/funktion/modul.

Finns det några lösningar på det faktiska problemet?

Som du sa, uppstår problemet endast när modul A laddas först. Lösningen är att förhindra det och skriva en extra modul som fungerar som en proxy till A och alla dess underklasser samtidigt som de importeras i rätt ordning.

Svar #2

I det här fallet föreslår jag att du tar bort beroendet av A -> B genom att skapa ett separat gränssnitt I. Både A och B behöver känna till I, och B behöver implementera det.

Under din laddningsprocess måste B berätta för A var man kan hitta en konstruktor eller fabrik för I (implementerad av B). Detta lämnar dig med dessa beroenden:

A -> I
B -> I
B -> A

Gränssnitt I kan se ut så här:

interface I {
  bFoo(): void;
}

export default I;

Class A skulle kunna se ut så här:

import I from "./i";

class A {
  private static __ICtor : new() => I;
  public static setIConstructor(ctor: new() => I) {
    A.__ICtor = ctor;
  }

  private __atSomePoint() : I {
    return new A.__ICtor();
  }
}

export default A;

Och slutligen klass B:

import I from "./i";
import A from "./a";

class B extends A implements I {
  public bFoo() {}
}

A.setIConstructor(B);

Enligt min åsikt skulle detta lösa ditt cykliska beroende, även om det är för sent vid det här laget.

Relaterade artiklar