Consider this code:
class Klass {
#priv = 42
get pub() {
return this.#priv
}
}
// these two correctly return 42
new Klass().pub;
(new class extends Klass {}).pub
// but this one throws "TypeError: Cannot read private member
// #priv from an object whose class did not declare it"
Object.create(new Klass()).pub // throws
// even though
Object.create(new Klass()) instanceof Klass // true
Object.getPrototypeOf(Object.create(new Klass())).pub // 42
I thought that since I have a true instance of the Klass
in the prototype chain then accessing the pub
wouldn’t throw.
Is this is by design? And if it is then why it was done this way?
Context:
I run into this issue when I was testing something with vitest. My code looked something like this:
import { it, expect } from 'vitest';
class Klass {
#priv = 42;
get pub() {
return this.#priv;
}
}
it('works', () => {
expect(new Klass()).toMatchObject({
make_this_test_fail: 'yup',
pub: 42,
});
});
My test obviously failed but instead of giving me nice diff showing that property make_this_test_fail
wasn’t found on the instance it showed
FAIL test/the.test.js [ test/the.test.js ]
TypeError: Cannot read private member #priv from an object whose class did not declare it
❯ Klass.get pub [as pub] test/the.test.js:7:15
5|
6| get pub() {
7| return this.#priv;
| ^
8| }
9| }
I tracked this to this line, where actual
comes from deepClone
, which creates instances of arbitrary classes using Object.create
here.