3

Sorry if this is bizarre or anti-pattern.

Suppose I have a static method on a child class, e.g (heavily simplified for readability)

class User extends Model {
  //...
    static getAll(){
      db.execute('SELECT * FROM users');
    }
}

Since I have multiple models I might want a getAll method on, it seems ideal to define getAll on the Model class, which makes reference to a tableName class variable. Ideally it would look something like

class Model {
  static getAll(){
    db.execute(`SELECT * FROM ${this.tableName}`);
  }
}
//...
User.tableName = 'users';

This won't work, because ES6 doesn't like you defining class variables like that. There are a number of workarounds, such as adding a tableName parameter in the parent then applying users to it in User:

class Model {
  static getAll(tableName) {
    db.execute(`SELECT * FROM ${tableName}`)
  }
}
//...
class User extends Model{
  static getAll() {
    Model.getAll('users')
  }
}

but explicitly re-writing inherited functions for all of a class's children like this seems dreadfully anti-pattern. Other solutions I've seen (such as using static functions to return constants, or wrapping both classes in an object) are kinda ugly and not a pattern I want to commit to unless I have to. So I'm looking for an easily-readable ES6 pattern that lets me do the following:

  1. Inherit static methods from a parent class.
  2. Refer to class variables (or something equivalent) in the parent class, such that they can be specified for different child classes
  3. Not have to explicitly refer to inherited methods in the child class.

Does such a solution exist, or is it worth doing it the 'hard way' in ES5?

John Hartman
  • 315
  • 2
  • 9
  • Could something like [this fiddle](https://jsfiddle.net/trnLf3rs/) be at least a little better? – Jaromanda X Nov 27 '17 at 05:51
  • It doesn't solve the problem of having to account for all of the parent's methods in each of the children, which is the main thing I'm trying to avoid. – John Hartman Nov 27 '17 at 05:56
  • I know, that's why I put it as a comment :p because I know it isn't an answer – Jaromanda X Nov 27 '17 at 05:56
  • Rereading your question, I think I misinterpreted it in my answer below. From second look, it sounds like you would want to use `Model.getAll` as opposed to `User.getAll`, right? If that's the case, I can't think of a way this is doable w/o a hack – Christian Santos Nov 27 '17 at 06:11
  • @ChristianSantos that's right - I want to use `Model`'s `getAll` method without having to redefine it on `User`. Fairly sure this is directly doable in Ruby and other OOPLs. Is there maybe some other way of effectively doing the same thing while keeping my DRY? Perhaps without ES6 class inheritance? – John Hartman Nov 27 '17 at 06:16
  • Modified my answer below -- my `User` no longer explicitly refers to any inherited methods. Does that work for your use case? – Christian Santos Nov 27 '17 at 06:24
  • "*This won't work, because ES6 doesn't like you defining class variables like that.*" - have you actually tried that? It works just fine, as long as you put the property assignment after the `class User` declaration. The only thing that does not work in ES6 is defining non-function values *inside* the `class` body. – Bergi Nov 27 '17 at 18:42

2 Answers2

2

This won't work, because ES6 doesn't like you defining class variables like that.

What makes you think that? User is a function like any other function. Static methods become properties of that function. Assigning a property directly to User works just fine.

class Model {
  static getAll() {
    return `SELECT * FROM ${this.tableName}`;
  }
}

class User extends Model {}
User.tableName = 'users';
class Product extends Model {}
Product.tableName = 'product';


console.log(User.getAll());
console.log(Product.getAll());
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
0

Although this feels slightly hackish -- there is a way to achieve what you need using these two facts about ES6 classes:

  • We can define static getters using static get in the class definition -- and these getters will function as static properties.
  • Static methods can use this to call other static methods. This means your parent class's static method can use this to refer to a static method in your child class.

Altogether, the code would look like this:

class Model {

    static getAll() {
        console.log(this.tableName); // will refer to child's static getter
    }

    static randomFunc() {
        console.log(this.tableName); // will also refer to child's static getter
    }
}

class User extends Model {
    
    static get tableName() {
        return "users"; // define child's tableName here
    }
}

class Worker extends Model {
    
    static get tableName() {
        return "workers"; // define child's tableName here
    }
}

User.getAll(); // prints users
User.randomFunc(); // prints users

Worker.getAll(); // prints workers
Worker.randomFunc(); // prints workers
Christian Santos
  • 5,386
  • 1
  • 18
  • 24
  • 1
    This looks like the least painful way of doing this: I didn't know about static getters, but it looks like they're only marginally different to proper class variables. It does work for my code, thanks! – John Hartman Nov 27 '17 at 06:37