Home > Enterprise >  Using .map() over a Map<K, V>() in TypeScript
Using .map() over a Map<K, V>() in TypeScript

Time:12-01

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.

  • Related