Avoiding circular imports

I’m having problem with circular imports. I have three files: one containing a super class and two that each contains a subclass of this super class.

SuperClass.js

export const SuperClass = class{
}

SubClass1.js

import { SuperClass } from './SuperClass'

export const SubClass1 = class extends SuperClass{
}

SubClass2.js

import { SuperClass } from './SuperClass'

export const SupClass2 = class extends SuperClass{
}

So far no problems. However, I would like to add a static method in SuperClass that creates new instances of SubClass1 and SubClass2. When I do that:

SuperClass.js

import { SubClass1 } from './SubClass1'
import { SubClass2 } from './SubClass2'

export const SuperClass = class{
  static getNewRandomSubClassInstance(){
    if(godIsHappy()){
      return new SubClass1()
    }else{
      return new SubClass2()
    }
  }
}

I get the error babel-runtime.js:32, Uncaught TypeError: Super expression must either be null or a function, not undefined.

The error comes from SubClass1.js, where we extend from SuperClass, which is undefined.

Does anyone have a nice solution to this problem?

1 Like

I think the simplest thing would be:

export const SuperClass = class{
  static getNewRandomSubClassInstance(){
    if(godIsHappy()){
      const SubClass1 = require('./SubClass1');
      return new SubClass1()
    }else{
      const SubClass2 = require('./SubClass2');
      return new SubClass2()
    }
  }
}

If using the imports/ directory then you may have to manually import the subclasses still so that they are included in the bundle (in non-circular code)

Thanks for the suggestion, but I’m trying to write native ES6 code (I don’t want to come back to it in a few year and face the same problem when Meteor stops transpiling code to ES5).

I think I’ll just create a new class in a new file and put getNewRandomSubClassInstance in that one. I guess the code will be better structured this way, but I get the feeling it will make life harder for the programmer (yet another class).

Hi @Peppe_LG,

I am interested in how you restructured your code to solve this… thanks!

I don’t remember the work around I used in this particular case, but the solution includes restructuring the code somehow, for example in the way I wrote in my earlier post.

An easier workaround, taking advantage of ES Module circular import resolution semantics is to add the static method after the class is defined:

import { SubClass1 } from './SubClass1'
import { SubClass2 } from './SubClass2'

export const SuperClass = class {
  // ...
}

SuperClass.getNewRandomSubClassInstance = function() {
  if (godIsHappy()) {
    return new SubClass1()
  } else {
    return new SubClass2()
  }
}

This way, when the sub-classes import the super class, the class without the static method is returned to them, and after the sub-classes have finished loading the static method(s) are added.

This works because classes are just sugar over objects and prototypes, and a static method is basically just a function on an object