When dealing with TypeScript and the MyMap
, there is an issue regarding its ability to recognize that each member of MyMap
with key K
is actually an array that allows you to use push
to add a value of type MyMap[K][number]
to it. It can understand this for individual instances of K
, but when K
is generic, things get more complicated. This is because TypeScript struggles to automatically generalize a list of details into a singular concept. Therefore, instead of seeing map[k].push
as one coherent method, it views it as a union of methods where any connection between map[k]
and cb
is lost.
To align your logic with the compiler's understanding, it is recommended to explicitly represent these operations in terms of generality. One approach outlined in microsoft/TypeScript#47109 involves starting from your existing MyMap
type:
type MyMap = {
type1: (() => void)[]
type2: ((data: string) => void)[]
}
const map: MyMap = { type1: [], type2: [] };
From here, you create a "base" version of the type, focusing solely on the elements within the arrays:
type MyMapBase = { [K in keyof MyMap]: MyMap[K][number] }
/* type MyMapBase = {
type1: () => void;
type2: (data: string) => void;
} */
With this base type set up, you can define addToMap()
as a generic function operating on MyMapBase
:
function addToMap<K extends keyof MyMap>(k: K, cb: MyMapBase[K]): void {
const m: { [K in keyof MyMap]: MyMapBase[K][] } = map;
m[k].push(cb);
}
This approach involves assigning map
to a variable m
that adheres to the mapped type
{[K in keyof MyMap]: MyMapBase[K][]}
. By structuring it in this way, the compiler can verify each property individually, allowing for successful implementation. Now,
m
is explicitly written as a type that abstracts over
MyMapBase
generically, enabling easy addition of values based on their respective types.
It would be more straightforward if we initially worked with MyMapBase
, as that is the underlying type being abstracted over. However, if you already have MyMap
defined elsewhere, this code demonstrates how you can still derive the necessary components from it.
Code Playground