One of the advantages of Javascript property syntax is that it allows to start implementation with simple "value"-type properties and only at a later point decide that a setter/getter property is necessary, without changing the API. Similarly there are use cases where a "value"-type property is sufficient for a base class, while its subclass may need setters/getters for maybe syntax checking. This is where I ran into problems with their corresponding constructors. Consider the following toy classes
class Base {
//prop; // uncommenting this line changes behavior. Sub property getter/setter for 'prop' will then be ignored
constructor(v) {
this.prop = v;
}
}
class Sub extends Base {
#prop;
constructor(v) {
super(v);
}
set prop(v) {
console.log("setting sub-prop " + v);
this.#prop = v;
}
get prop() {
console.log("getting sub-prop " + this.#prop);
return this.#prop;
}
}
const base = new Base(10);
const sub = new Sub(20); // TypeError
This snippet leads to an error "TypeError: can't set private field: object is not the right class". Similarly in Firefox it says: "Uncaught TypeError: can't set private field: object is not the right class". I have a couple of issues with this error (they all boil down to the question: "why is this error thrown?"):
- at the place where it is thrown (when the super-constructor calls the setter for "prop" defined in
Sub
),this
already carries the Sub-prototype in its property__proto__
, so it looks to me to be "in the right class" - if the constructor were not a constructor but instead a method (say
test()
) of the base class, we could do the call from the Sub-instance sub without an error thrown:sub.test()
. - I understand that it is bad practice to use virtual methods in constructors in particular of base classes (i.e. methods that can be overriden in subclasses). However, in this case the base class rather innocently sets a member and it is really the SubClass responsibility to define getters/setters that are consistent with a property of type value. One might argue that the Error prevents the user to do inconsistent overridings, but then one could simply replace the private member "#prop" by a pseudo-private (but actual public) member "_prop" and it would be allowed and work as expected. So I don't see a benefit of this Error been thrown.
So why is this error thrown, and how should one deal with it, if maybe the base class is not under one's own control?
P.S.: Interestingly, the type error does not appear if in the base class Base the property "prop" is explicitly declared, as commented out in the code. However, in this case the setter defined in the Sub prototype is simply ignored and the newly generated Object of type "Sub" will have the property "prop" with a descriptor of type "value". This 'puzzle' is not part of this question (it might become a separate one, if I can't figure it out), but in the context of this question I find it at least worth mentioning.