Search code examples
javascriptclasshierarchy

Is it possible to access all levels in javascript class hierarchy?


So I'm curious if it's possible from a base (parent) class to access static properties from all levels in a class hierarchy.

Let's paint an example:

class Tier1 {
  static NUMBERS = ['one', 'two'];

  getNumbers() {
    // how to do here?
    return this.constructor.NUMBERS.join(', ');
  }
}

class Tier2 extends Tier1 {
  static NUMBERS = ['three'];
}

class Tier3 extends Tier2 {
  static NUMBERS = ['four', 'five'];
}

class Tier4 extends Tier3 {
  static NUMBERS = ['six'];
}

const t1 = new Tier1(); t1.getNumbers(); // Expect to get "one, two"
const t2 = new Tier2(); t2.getNumbers(); // Expect to get "one, two, three"
const t3 = new Tier3(); t3.getNumbers(); // Expect to get "one, two, three, four, five"
const t4 = new Tier4(); t4.getNumbers(); // Expect to get "one, two, three, four, five, six"

Is it possible to achieve this effect? Can I traverse down the class instance hierarchy from this in a dynamic way?

Would love some input and ideas here


Solution

  • You can do it by creating a new static NUMBERS variable at each level that is a copy of the previous one and adding the new items, as follows:

    class Tier1 {
      static NUMBERS = ['one', 'two'];
    
      getNumbers() {
        return this.constructor.NUMBERS.join(', ');
      }
    }
    
    class Tier2 extends Tier1 {
      static NUMBERS = [...this.NUMBERS, 'three'];
    }
    
    class Tier3 extends Tier2 {
      static NUMBERS = [...this.NUMBERS, 'four', 'five'];
    }
    
    class Tier4 extends Tier3 {
      static NUMBERS = [...this.NUMBERS, 'six'];
    }
    
    const t1 = new Tier1(); console.log(t1.getNumbers()); // Shows "one, two"
    const t2 = new Tier2(); console.log(t2.getNumbers()); // Shows "one, two, three"
    const t3 = new Tier3(); console.log(t3.getNumbers()); // Shows "one, two, three, four, five"
    const t4 = new Tier4(); console.log(t4.getNumbers()); // Shows "one, two, three, four, five, six"

    Or if you need to have a separate NUMBERS array in each class, and combine them as late as possible (e.g. because their contents could be mutable), then you can follow the approach below. I think this also answers "Can I traverse down the class instance hierarchy from this in a dynamic way?" with a definite "yes".

    class Tier1 {
      static NUMBERS = ['one', 'two'];
    
      gatherAllNumbers() {
        const constructors = this.getConstructors();
        return constructors.reverse().map(c => c.NUMBERS).flat();
      }
      
      getConstructors() {
        const result = [];
        let next = Object.getPrototypeOf(this);
        while (next.constructor.name !== "Object")
        {
          result.push(next.constructor);
          next = Object.getPrototypeOf(next);
        }
        return result;
      }
      
      getNumbers() {
        return this.gatherAllNumbers().join(', ');
      }
    }
    
    class Tier2 extends Tier1 {
      static NUMBERS = ['three'];
    }
    
    class Tier3 extends Tier2 {
      static NUMBERS = ['four', 'five'];
    }
    
    class Tier4 extends Tier3 {
      static NUMBERS = ['six'];
    }
    
    const t1 = new Tier1(); console.log(t1.getNumbers()); // Shows "one, two"
    const t2 = new Tier2(); console.log(t2.getNumbers()); // Shows "one, two, three"
    const t3 = new Tier3(); console.log(t3.getNumbers()); // Shows "one, two, three, four, five"
    const t4 = new Tier4(); console.log(t4.getNumbers()); // Shows "one, two, three, four, five, six"