Наследование TypeScript и циклические зависимости в SystemJS

Я использую TypeScript с --module system (SystemJS) в очень большом проекте. SystemJS поддерживает циклические зависимости и в большинстве случаев работает нормально. Однако, когда в дело вступает наследование TypeScript, все начинает ломаться.

Например, если class A зависит от class B, а class B наследуется от class A, то если class A загружается первым:

  1. Он приостановит разрешение class A's и попытается загрузить зависимость class B
  2. class B будет думать, что его зависимости разрешены, так как class A было затронуто.
  3. Наследование class B's не разрешится, потому что class A все еще не определено.

Большинство «решений», которые я могу найти в Интернете, для циклических зависимостей с загрузчиками модулей:

  • Измените свой дизайн/объедините классы в один модуль
  • Обходные пути для CommonJS и не для TypeScript

Я чувствую, что существуют веские основания для кругового дизайна, и объединение классов в гигантские файлы не всегда желательно, поэтому, пожалуйста, считайте, что эти обходные пути не относятся к теме вопроса, который я задаю.

Есть ли решения актуальной проблемы?


person Eric    schedule 18.05.2016    source источник


Ответы (3)


Изменение дизайна – самое выгодное решение. Класс не должен зависеть от своих подклассов. Если вы используете их на фабрике или около того, это отдельная проблема и должна быть в отдельном классе/функции/модуле.

Есть ли решения актуальной проблемы?

Как вы сказали, проблема возникает только тогда, когда модуль A загружается первым. Решение состоит в том, чтобы предотвратить это и написать дополнительный модуль, который действует как прокси для A и всех его подклассов, импортируя их в правильном порядке.

person Bergi    schedule 18.05.2016

В этом случае я предлагаю вам удалить зависимость A -> B, создав отдельный интерфейс I. И A, и B должны знать I, а B нужно его реализовать.

В процессе загрузки B должен сообщить A, где найти конструктор или фабрику для I (реализовано B). Это оставит вас с этими зависимостями:

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

Интерфейс I может выглядеть так:

interface I {
  bFoo(): void;
}

export default I;

Class A может выглядеть так:

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;

И, наконец, class B:

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

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

A.setIConstructor(B);

ИМХО, это решит вашу циклическую зависимость, даже если уже слишком поздно.

person sgrtho    schedule 15.07.2016

Есть отличный способ решить эту проблему с помощью плагина преобразования Babel: https://github.com/zertosh/babel-plugin-transform-inline-imports-commonjs

Что он делает, так это преобразует импорт модуля в начале файла во встроенные требования, которые фактически импортируют/требуют другие модули непосредственно перед их использованием.

В большинстве случаев это решает проблему автоматически, так как к моменту фактического запуска любого кода, использующего классы, все модули завершили свой экспорт.


Обратите внимание, что по умолчанию приведенный выше плагин применяется ко всем импортам в вашем проекте, превращая их все во встроенные требования. Однако более серьезная проблема заключается в том, что я не смог найти встроенный способ заставить его работать с импортом/требованиями относительного пути.

Я исправил обе эти проблемы в своем форке проекта здесь: https://github.com/Venryx/babel-plugin-transform-inline-imports-commonjs

Я сделал запрос на включение этих изменений, но он ожидает рассмотрения в банкомате.

person Venryx    schedule 07.01.2017