I would like to extend the TypeScript Map
type in my react TypeScript app to allow me to use .map()
, in a similar way as you might with an Array
.
I found this post that describes several methods, but the most natural to me for TypeScript is the answer by Jeremy Lachkar, although it isn't very popular. It means I don't have to introduce any awkward syntax in the client code and can just extend the Map
object.
His code looks like this:
export {}
declare global {
interface Map<K, V> {
map<T>(predicate: (key: K, value: V) => T): Map<V, T>
}
}
Map.prototype.map = function<K, V, T>(predicate: (value: V, key: K) => T): Map<K, T> {
let map: Map<K, T> = new Map()
this.forEach((value: V, key: K) => {
map.set(key, predicate(value, key))
})
return map
}
I have augmented this to make it, for me, what seems more natural (I think the Map<V, T>
in the interface might be a mistake in any case):
export {}
declare global {
interface Map<K, V> {
map<T>(predicate: (key: K, value: V) => T): Map<K, T>;
}
}
Map.prototype.map = function<K, V, T>(predicate: (key: K, value: V) => T): Map<K, T> {
let map: Map<K, T> = new Map();
this.forEach((key: K, value: V) => {
map.set(key, predicate(key, value));
});
return map;
}
This works great. I can now map over my Map
and produce a new one. However, now none of the other methods on Map
, such as .set()
, are available to me, and my code throws errors. These are now undefined
.
Any idea how to deal with this? I'm imagining in my little head that this might be a tsconfig.json
issue of some sort, but I don't know.
CodePudding user response:
class Map2<K, V> extends Map<K, V> {
map<T>(mapper: (value: V, key: K, map: this) => T): Map2<K, T> {
let m = new Map2<K, T>;
for (let [k, v] of this.entries()) {
m.set(k, mapper(v, k, this))
}
return m;
}
}
let m = new Map2([[1, 2], [3, 4]])
console.log(m)
let m2 = m.map(e => e 10)
console.log(m2)
Making a subclass is generally a better idea then changing prototypes (tho that works well if you are the only one who changes prototypes)
declare global {
interface Map<K, V> {
map1<T>(mapper: (value: V, key: K, map: this) => T): Map2<K, T>
}
}
Map.prototype.map1 = Map2.prototype.map
export {}
CodePudding user response:
Ok, turns out it was my client code that was wrong, and that is why the .set()
and so on were undefined. So the original post I referenced right at the top does it fact work.
I had imagined it was because redefining the interface had removed the other methods. Not so. Silly me.
As you were.