Error expected when super constructors invoke private methods in derived classes?

I've noticed error when a constructor in an inherited class leads to the invocation of a derived private method. Instead of being able to call the method, it fails with a TypeError. Example:

class Base {
    constructor () {
        this.method()   
    }
    method() {}
}

class Derived extends Base {
    #priv(){}
    method() {
        this.#priv()
    }
}

new Derived()
// Chrome 97: Uncaught TypeError: Object must be an instance of class Derived
// Firefox 97: Uncaught TypeError: can't access private field or method: object is not the right class
// Safari 15.3: TypeError: Cannot access private method or acessor (evaluating 'this.#priv')

Shouldn't the instance in Base's constructor be a Derived object when constructing via the Derived constructor?

I originally noticed this trying to extend Map.

class MyMap extends Map {
    #log(name){ 
        console.log(this, 'called', name)
    }
    set(key, value) {
        this.#log('set')
        return super.set(key, value)
    }
}
new MyMap([['a', 1]]) // TypeError ...

This happens with each of the keyed collections because they each internally call set() in their constructors (assuming they're provided iterables to pull from).

Yes, this is expected, because the private fields aren’t installed until super is finished. Which means, you explicitly can’t have the superclass invoke code in the subclass that depends on having a fully constructed instance.

1 Like

Across many languages the general advise seems to be: avoid calling virtual functions from a constructor.

If the constructor needs something from a subclass it can be passed in explicitly as a constructor argument.

2 Likes

Should keyed collection constructors not be calling set() then?

There are people who think that was a mistake yes.

1 Like

After recently trying to subclass Map with a private field, I whole heartedly concur on that. It's at best useless.

Interesting. This is probably a case where it would be better to use composition instead of inheritance.