Question
Why objects having instance of a class with private property in prototype throw when accessing private member?
Consider this code:
class Klass {
#priv = 42
get pub() {
return this.#priv
}
}
// these two correctly return 42
console.log(new Klass().pub);
console.log((new class extends Klass {}).pub)
// but this one throws "TypeError: Cannot read private member
// #priv from an object whose class did not declare it"
console.log(Object.create(new Klass()).pub) // throws
// even though
console.log(Object.create(new Klass()) instanceof Klass) // true
console.log(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 Object.create(new Klass()).pub
wouldn't throw.
Is this is by design? And if it is then why it was done this way?
Also, what is a correct way to have a generic function to clone arbitrary class instances that behave similar to Klass
?
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.