I’m trying to enhance the Map
class in JavaScript by adding a TypedMap
subclass that extends the Map
superclass.
TypeMap.js
export default class TypedMap extends Map {
#keyType;
#valueType;
constructor(keyType, valueType, entries) {
if (entries) {
for (let [k, v] of entries) {
if (typeof k !== keyType || typeof v !== valueType) {
throw new TypeError(`Wrong type for entry [${k}, ${v}]`);
}
}
}
super(entries);
this.#keyType = keyType;
this.#valueType = valueType;
}
set(key, value) {
if (this.#keyType && typeof key !== this.#keyType) {
throw new TypeError(`${key} is not of type ${this.#keyType}`);
}
if (this.#valueType && typeof value !== this.#valueType) {
throw new TypeError(`${value} is not of type ${this.#valueType}`);
}
return super.set(key, value);
}
}
main.js
import TypedMap from './TypedMap.js';
let entries = [
[1, 2],
[3, 4],
[5, 6],
];
let typedMap = new TypedMap('number', 'number', entries);
Error I’m getting
Uncaught TypeError: Cannot read private member #keyType from an object whose class did not declare it
at TypedMap.set (TypedMap.js?t=1696367023223:20:14)
at new Map (<anonymous>)
at new TypedMap (TypedMap.js?t=1696367023223:13:5)
at main.js?t=1696367092683:9:16
The #keyType
and #valueType
fields are private but I should still access them from within the class TypedMap
but somehow that doesn’t happen to be the case here.
I think it has something to do with the overridden set
method because I added a test
method in the TypedMap
class and could access the private fields.
Can anyone explain what is happening here?
2
Answers
You have the problem that you call
super(entries)
which callsthis.set()
which needs your private properties. But they aren’t defined yet inthis
.The error message in Chrome is kind of misleading, in Firefox it’s more meaningful:
That could mean that
this
isn’t in a proper state ("not the right class"): the private properties are declared but not yet defined.On the other hand you cannot access
this
before callingsuper()
to define your private props sincesuper()
actually provides a properthis
with a proper prototype chain.So it’s a deadlock. So add your entries manually.
Btw you can remove your type checking in the constructor since it’s done in
set()
.Adding to the already provided answer …
… the OP might think about implementing
TypedMap
with just an additionally provided single validator function.The advantage comes with more flexibility of how to handle types in terms of …
key
orvalue
) could easily implement checks for multiple valid types.Furthermore, any implementation should also just extend the arguments signature of the extended type. Thus as for
TypedMap
versusMap
one would choose for the former a signature of …constructor(entries, keyType, valueType) { ... }
in case of the OP’s examlpe code,constructor(entries, validateEntry) { ... }
in case of the here proposed approach.