How to move typescript classes with circular dependency into separate files

Problem

The problem is that Webpack, that is internally used by Angular CLI, has strict rules about imports by its architecture. The order of the imports is important, and if the final JavaScript code has a circular dependency (module 1 imports module 2 which imports module 1) then the import mechanism is broken.

It's interesting that not every time you import some circular dependency it will become a problem. Circular dependency is a runtime JavaScript problem, not the TypeScript's problem. So, the main criteria is: do not let the imported circular dependency come through to JavaScript. That means: use them as types, but don't compare to them, don't call them, don't pass them as functions arguments, etc.

Valid usage of circular dependency (doesn't lead to an error):

const a: SubClass1 = this; // type casting -> TS only
const b = <SubClass1>this; // type casting -> TS only

Why doesn't this lead to a JavaScript circular dependency? Because after TypeScript gets compiled this SubClass1 will disappear: JavaScript does not have a type declaration.

Invalid usage (leads to circular dependency error):

const c = instanceof SubClass1 // passing as argument -> JS
const d = new SubClass1() // calling -> JS
const e = SubClass1 // saving as value -> JS

And now coming back to the original problem:

doSomething() {
  if (this.children[0] instanceof SubClass1) {
    (this.children[0] as SubClass1).action1();
  }
}

To be precise, it is only at this line if (this.children[0] instanceof SubClass1) { because the line below where you cast the type will be cut off after typescript compilation. The instanceof in a base class is a typical reason for a circular dependency and there are multiple ways to resolve it. Mostly, you need to get rid of instanceof in favour of something else.

Solution 1. Add a qualifier property on each subclass and force it to be implemented:

export abstract class BaseClass {

  abstract get qualifier(): string;

  doSomething() {
    if (this.qualifier === 'SubClass1') { ... }
  }

}

export class SubClass1 extends BaseClass {

  get qualifier() {
    return 'SubClass1';
  }

}

Ugly, yet functionable. Helps in case there are lots of classes and you need to distinguish between all at any moment.

Solution 2. Add a e.g. boolean qualifier, that clearly describes a particular intention (typical example is some group of the classes requires the same feature that is implemented in superclass), and instantiate it with some default value:

export abstract class BaseClass {

  get isDuck() {
    return false; // by default is not a duck
  }

  doSomething() {
    if (this.isDuck) { ... }
  }

}

// this one is a duck
export class SubClass1 extends BaseClass {

  get isDuck() {
    return true;
  }

}

// this one is not a duck. Why the hell should it be a duck? :D
export class SubClass2 extends BaseClass {}

The second solution is a little bit better because the qualifying is not done by a superclass, but by a child class itself, so the child class defines who / what it is in a more granular manner. The only thing that superclass does in this case is deciding based on a functional property what to do.

Solution 3. Simply move the logic away to the children classes; let each of them implement its logic

export abstract class BaseClass {
  children: BaseClass[];

  abstract loadFromModel(model: StorageModel);

  abstract doSomething(): void;
}

export class SubClass1 extends BaseClass {
  action1() {
  }

  doSomething() {
    this.action1();
  }
}

This one is the most trivial, however not always sufficient.