javascriptnode.jstypeerrorprivate-membersclass-fields

Cannot read private member from an object whose class did not declare it...?


In this program:

class Example {

  #privateMember = 123;

  // these are fine  
  addNumber (n) { return this.#privateMember + n; }
  doAddNumber (n) { return this.addNumber(n); }

  // "cannot read private member #privateMember from an 
  // object whose class did not declare it"
  #operations = { add: this.addNumber };
  operate (n) { return this.#operations.add(n); }

}

const ex = new Example();
console.log(ex.addNumber(77));
console.log(ex.doAddNumber(77));
console.log(ex.operate(77));

Calling addNumber works fine, so does doAddNumber, but calling operate yields the error:

TypeError: Cannot read private member #privateMember from an object whose class did not declare it
    at Object.addNumber [as add] (<anonymous>:11:17)
    at Example.operate (<anonymous>:20:29)
    at <anonymous>:27:16
    at dn (<anonymous>:16:5449)

I can't make any sense of this error because:

  1. addNumber works fine, so its not a syntax error or a typo at least.
  2. doAddNumber works fine, so its not a problem calling functions from other functions.
  3. operate just calls addNumber which, from (1), works fine.
  4. this is an object whose class declares #privateMember... I mean, this is an Example and I can see that it's declared in Example. It's right there... I typed it myself...

I found TypeError: Cannot read private member from an object whose class did not declare it but I can't understand how it applies, if it applies.

I can't figure out what's going on here. Why doesn't operate work even though addNumber and doAddNumber do?

In my real code (this is just a minimal example), I am trying to use a dictionary like #operations to hold implementations of a number of various algorithms for performing a task, indexed by a string ID, where the string algorithm ID is specified to the constructor. This is also convenient because I can get the keys from this dictionary to provide a list of valid algorithm IDs without having to duplicate that list anywhere. Now, I can just switch it to an if statement and make sure I keep the queryable list up to date as well, but I can't understand why this doesn't work.


Solution

  • When you set #operations.addNumber and then call it, this === #operations, not ex, which fails because ex.#operarions.#privateMember does not exist.

    If I change your Example class with

    #operations = { add: this.addNumber.bind(this)};
    

    then ex.#operations.add runs with this === ex and your code returns

    > 200
    > 200
    > 200