17

In Javascript, with the following illustration code:

class Base {
   constructor() {  this._val = 1   }
   get val()     { return this._val }
}

class Xtnd extends Base {
   set val(v)    { this._val = v }
}

let x = new Xtnd();
x.val = 5;
console.log(x.val);  // prints 'undefined'

the instance x will not inherit get val()... from Base class. As it is, Javascript treat the absence of a getter, in the presence of the setter, as undefined.

I have a situation in which I have many classes that all have the exact same set of getters but unique setters. Currently, I simply replicate the getters in each class, but I'm refactoring and want to eliminate the redundant code.

Is there a way to tell JS to keep the getter from the base class, or does anyone have an elegant solution to this problem?

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
codechimp
  • 1,509
  • 1
  • 14
  • 21
  • For future reference, this question has nothing to do with the [tag:redundancy] tag, which refers to intentional duplication of code for critical sections to be more robust. I'm pretty sure you intended to use [tag:boilerplate] which refers to seemingly unnecessary duplication of code. – Patrick Roberts Dec 02 '18 at 21:38
  • "*I have a situation in which I have many classes that all have the exact same set of getters but unique setters.*" - do you actually need inheritance for that? JS has lots of other ways for sharing code if all you want to avoid is duplication. – Bergi Dec 02 '18 at 22:28
  • 1
    "*Is there a way to tell JS to keep the getter from the base class*" - [No, it's by design](https://stackoverflow.com/q/28950760/1048572). – Bergi Dec 02 '18 at 22:34

1 Answers1

17

This limitation is due to how JavaScript treats property accessors behind the scenes. In ECMAScript 5, you end up with a prototype chain that has a property descriptor for val with a get method defined by your class, and a set method that is undefined.

In your Xtnd class you have another property descriptor for val that shadows the entire property descriptor for the base class's val, containing a set method defined by the class and a get method that is undefined.

In order to forward the getter to the base class implementation, you'll need some boilerplate in each subclass unfortunately, but you won't have to replicate the implementation itself at least:

class Base {
   constructor() {  this._val = 1   }
   get val()     { return this._val }
}

class Xtnd extends Base {
   get val()     { return super.val }
   set val(v)    { this._val = v }
}

let x = new Xtnd();
x.val = 5;
console.log(x.val);  // prints '5'
David Sickmiller
  • 1,145
  • 8
  • 8
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • This semi-satisfying solution works. Thanks. ... but holding out hope for slightly more elegant solution. – codechimp Dec 03 '18 at 18:18
  • @codechimp are you using a transpiler? If so I could add another solution using a class decorator and/or a method decorator to forward the property accessor(s) to the base class. Does that sound like something that would benefit you? – Patrick Roberts Dec 03 '18 at 18:35
  • Transpilier is a solution that I have thought of, but I'm resisting using one. I understand the value of it, but I want to keep this code base pure vanilla JS. – codechimp Dec 04 '18 at 19:19
  • `this._val = v` jars me. I would write `super._val = v`. – ceving Jun 18 '23 at 16:33
  • @ceving that comment would be more appropriate on the question, I intentionally did not change that so that my answer would not obscure the only part of the code that really needed to change in order to achieve what was intended. – Patrick Roberts Jun 18 '23 at 17:06