Могут ли внешние модули typescript иметь круговые зависимости?
Похоже, это не разрешено. requireJS выдает ошибку при следующем (этот пост отличается, поскольку он был разрешен с помощью внутренних модулей):
element.ts:
import runProperties = require('./run-properties');
export class Element {
public static factory (element : IElement) : Element {
switch (element.type) {
case TYPE.RUN_PROPERTIES :
return new runProperties.RunProperties().deserialize(<runProperties.IRunProperties>element);
}
return null;
}
}
Run-properties.ts:
import element = require('./element');
export class RunProperties extends element.Element implements IRunProperties {
}
Ответы
Ответ 1
Нет, модули не могут иметь круговые зависимости, если они не находятся в одном файле. Каждый файл обрабатывается последовательно, синхронно, поэтому полное определение файла (включая все экспортные данные, например) не было завершено, когда оно переходит во второй файл, который сразу же пытается потребовать/ссылаться на первый файл и т.д.,
Обычно вы можете разбить циклическую зависимость, введя интерфейс или базовый класс в общий файл определения (в основном, только интерфейсы), а другие файлы используют это как общий "интерфейс", а не напрямую ссылаются на классы, Это типичный шаблон на многих платформах.
Ответ 2
У меня такая же проблема, я смог ее исправить, создав класс factory, который позволяет регистрировать дочерние классы и использовать Generics для создания экземпляра.
Ссылка: https://www.typescriptlang.org/docs/handbook/generics.html#using-class-types-in-generics
См. пример кода ниже:
Базовый класс (abstract.control.ts)
export type AbstracControlOptions = {
key?:string;
}
export abstract class AbstractControl {
key:string;
constructor(options:AbstracControlOptions){
this.key = options.key;
}
}
Родительский класс (container.ts)
import { AbstractControl, AbstracControlOptions } from './abstract.control';
import { Factory } from './factory';
export { AbstracControlOptions };
export abstract class Container extends AbstractControl {
children: AbstractControl[] = [];
constructor(options: AbstracControlOptions) {
super(options);
}
addChild(options: { type: string }) {
var Control:any = Factory.ControlMap[options.type];
if (Control) {
this.children.push(Factory.create(Control, options));
}
}
}
Мне больше не нужно импортировать дочерние классы, потому что я использую factory.ts для создания экземпляров дочерних классов.
Factory Класс (factory.ts)
import {AbstractControl, AbstracControlOptions} from './abstract.control';
type ControlMap<T extends AbstractControl> = {
[type:string]:T
};
export class Factory{
static ControlMap: ControlMap<any> = {};
static create<T extends AbstractControl>(c: { new ({}): T; }, options: AbstracControlOptions): T {
return new c(options);
}
}
Хотя конструктор классов, по-видимому, вызывается в c: { new ({}): T }
, но на самом деле его не вызывает. Но получает ссылку на конструктор с помощью оператора new
. Параметр {}
для конструктора в моем случае требуется, потому что это требует базовый класс AbstractControl
.
(1) Детский класс (layout.ts)
import { Factory } from './factory';
import { Container, AbstracControlOptions } from './container';
export type LayoutlOptions = AbstracControlOptions & {
type:"layout";
}
export class Layout extends Container {
type: string = "layout";
constructor(options:LayoutlOptions) {
super(options);
}
}
Factory.ControlMap["layout"] = Layout;
(2) Детский класс (повторитель .ts)
import { Factory } from './factory'
import { Container, AbstracControlOptions } from './container';
export type RepeaterOptions = AbstracControlOptions & {
type: "repeater";
}
export class Repeater extends Container {
type: string = "repeater";
constructor(options:RepeaterOptions) {
super(options);
}
}
Factory.ControlMap["repeater"] = Repeater;