2

I have two classes in ES6 that need each other. The code is here (I'm using Meteor.js);

export class Activity {
  constructor(doc) {
    _.extend(this, doc);
  }
  getSubActivities() {
    return Activities.find({ parent: this._id }).fetch();
  }
}

class ActivitiesCollection extends Mongo.Collection {
  /* nothing relevant */
}

export const Activities = new ActivitiesCollection('appjournalActivities', {
  transform(doc) {
    return new Activity(doc);
  },
});

The code works properly. But the linter does not like that I use Activities before it has been defined (error description at eslint.org).

Am I using a wrong pattern/code structure? Is there a standard solution in ES6?

One solution is the following (it works):

export let Activities = null;
/* code as before */
Activities = new ActivitiesCollection('appjournalActivities', {
/* code as before */

But I do not like it very much.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
PeptideChain
  • 543
  • 3
  • 16
  • 1
    I believe this is standard behavior in other languages. If two classes depend on each other, the names need to be declared before using them. In C++ this is called forward declaration (http://stackoverflow.com/a/994262/3402854). JS may have a solution for this, but I am not aware of one. – cyberbit May 10 '16 at 17:17
  • 2
    Your case is similar with a singleton pattern. Try to send Activities to constructor of Activity: new Activity(doc, Activities) – Ivan Rave May 10 '16 at 17:34
  • Idk, JS has Function Hoisting, and classes are syntatic sugar for functions because of prototypical inheritance, so it should work without respecting Forward Declaration. – eduardogbg May 10 '16 at 17:38
  • 1
    No, this should be define with nearly default ESLint config: `{ "extends": ["eslint:recommended"], "ecmaFeatures": { "modules": true } , "env": { "es6": true }, "parserOptions": { "sourceType": "module" } }` – Estus Flask May 10 '16 at 17:41
  • @ivan-rave thank you, nice solution – PeptideChain May 10 '16 at 18:04
  • @estus: thank you, "eslint:recommended" is what was needed in the configuration – PeptideChain May 10 '16 at 18:04

1 Answers1

2

The code works properly. But the linter does not like it.

Then screw the linter, or disable that particular rule :-)

Am I using a wrong pattern/code structure?

No. This is pretty much unavoidable if you have circular dependencies.

Is there a standard solution in ES6?

You could put them in separate modules, which just lifts the circular dependencies from the scope to the module level but might not be detected by es-lint.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The reason you would use a linter is because you want to conform to certain coding standard, disabling a rule because you can't get away with something seems like a slippery slope. In this case there are other ways to get the desired results without disabling the linter. – pgpb.padilla May 11 '16 at 00:36
  • @pgpb.padilla: The solution proposed in the question is actually much worse than disabling the linter - it throws exceptions just the same, but it doesn't get caught by the linter. Imo, if coding standards get in the programmers way of writing correct code, then rather the standards should be adapted than using error-prone workarounds. – Bergi May 11 '16 at 01:16
  • I think relying on hoisting for your code to work is pretty bad, the linter is warning about that which I think is a good thing, that's part of the reason to have a linter in the first place, to stop you from relying on the badly implemented parts of JS. – pgpb.padilla May 11 '16 at 01:22
  • 1
    @pgpb.padilla: I consider hoisting (especially of functions) to be one of the good parts of JS :-) It makes something like mutually recursive functions so much easier and painless to declare. – Bergi May 11 '16 at 01:31