Search code examples
typescripttypescript3.0

TypeScript: Copy Map while adding a key-value-pair at the same time


I want to elegantly copy a typescript struct interfaceName (which satisfies a certain interface). This TypeScript contains a map attribute3 of Type Map<IKey, number>, which perhaps has to be modified (depending on a condition): An additional key value pair has to be inserted.

IKey, again, is an interface.

interface InterfaceName{
   attribute1: string
   attribute2: string
   attribute3: Map<IKey, number>

}

interface IKey{
    a1: number
    a2: number
}

}

My attempt was the following: I just use the ... syntax to copy all the members of interfaceName and then try to assign attribute3 a modified copy. But somehow this does not work: In the end the same Map object is passed out of the function, regardless of the boolean.

const createModifiedCopyIfWished = (interfaceName: InterfaceName, wished: Boolean) => wished ? {
        ...interfaceName,
        attribute3: interfaceName.attribute3.set({a1:1,a2:2},5)
    }
    :
    interfaceName

let a: InterfaceName = {
    attribute1: "a",
    attribute2: "b",
    attribute3: new Map<IKey, number>()
}

let b = createModifiedCopyIfWished(a, true)

// {"attribute1":"a","attribute2":"b","attribute3":{}}
console.log(JSON.stringify(b))

What is the prober way to do this? Is it even possible within one statement?

Working example: LiveCode


Solution

  • Yes, it should be possible with one statement, like

    const createModifiedCopyIfWished = (
      interfaceName: InterfaceName, 
      wished: boolean
    ) => wished ? {
      ...interfaceName,
      attribute3: new Map(
        [...Array.from(interfaceName.attribute3), [{ a1: 1, a2: 2 }, 123]]
      )
    } : interfaceName
    

    This is assuming you're trying to clone the map and not just modify the existing one.


    Be warned, though, IKey is probably not a good key type for a Map. Map uses object equality based on identity (same exact object in memory) and not any kind of "same properties" equality. So, the following will not behave as one might expect:

    const map: Map<IKey, number> = new Map();
    map.set({ a1: 1, a2: 2 }, 123);
    const val = map.get({ a1: 1, a2: 2});
    console.log(val); // undefined !
    

    That's because ({a1: 1, a2: 2} === {a1: 1, a2: 2}) is false. Unless you're very careful, using objects as map keys is a good way to lose them. The only way objects as map keys works is to store the actual object keys somewhere and use them later to look up entries in the map. And that's usually not what people want.

    More reasonable behavior is probably just to use a string as your key and convert your intended key with something like JSON.stringify(objectKey) before putting it into the map or looking it up.


    Anyway hope that helps. Good luck.