Need explanation of error message 'TypeError: Receiver must be an instance of class' [duplicate]
Asked Answered
S

1

7

When I run this code:

'use strict';

class Base {
  constructor() {
    this._public();
  }
}

class Child extends Base {
  constructor() {
    super();
  }

  _public() {
    this.#privateMethod();
  }

  #privateMethod() {
    this.bar = 1234;
  }
}

const c = new Child();
console.log(c.bar);

I get the following error:

   this.#privateMethod();
         ^

TypeError: Receiver must be an instance of class Child

As I understand JavaScript, the receiver in the following code is exactly an instance of the class Child. It's even shown in the dev tools inspector:

dev-tools screenshot

So could someone please explain to me what's going on? Is it a bug or what?


Actually FF shows a more accurate error message:

Uncaught TypeError: can't access private field or method: object is not the right class
Saccharin answered 10/2, 2023 at 18:23 Comment(0)
E
7

This is one example of why it's best not to call public instance methods from constructors. When your Base class constructor calls this._public(), Child's initialization is not (yet) complete. The earliest you can use Child members is after super() has returned, not before. It's only after super() returns that Child's automatic initialization (such as setting up private fields and methods, and initializing public properties defined via class fields syntax) is complete. You can see this with public class fields as well:

class Base {
    constructor() {
        console.log("test" in this); // false
    }
}

class Child extends Base {
    test; // <== Public class field
    
    constructor() {
        super();
        console.log("test" in this); // true
    }
}

new Child();

You're okay to call this._public() from Child's constructor just after super() returns (although, again, it's best not to call public methods from constructors! better to call #privateMethod directly), because Child will be fully ready by then (other than any initialization you're doing in its constructor, of course), but not before super() returns.

Enmity answered 10/2, 2023 at 18:29 Comment(4)
Thank you, sadly, this topic was not present in your book, 'The new toys'. I re-read pieces from it twice a month.Saccharin
@DmitryKoroliov - Drat!! :-) Sorry. But hopefully it was useful in other respects.Enmity
the crucial point, as I understood, is that the private members #foo are present on the instance itself, not in the prototype chain. That's why they are not accessible in the constructors. When I spelt that (where the private members are present), it looked so obvious, that I'm ashamedSaccharin
@DmitryKoroliov - FWIW, IMHO, absolutely no cause for shame. But you're right, private members aren't on the prototype, and you're spot-on that that's part of what matters here. Happy coding!Enmity

© 2022 - 2024 — McMap. All rights reserved.