typescriptdictionarypredicate

TypeScript: Populating a Map with Strings and Predicates from String/String and String/Predicate Map


I have a Map of string to string:

const strToStr = new Map<string, string>([
    ['foo', 'foo'],
    ['bar', 'qux'],
]);

I also have a Map of string to a predicate function accepting a string:

const strToFnc = new Map<string, (_: string) => boolean>([
    ['baz', (_: string) => true],
    ['buz', (_: string) => false],
]);

A third Map shall store string predicate functions by string:

const predicates = new Map<string, (_: string) => boolean>();

For the string/string entries, the predicate function is made up and added:

strToStr.forEach((k) => predicates.set(k, (_) => (k == strToStr.get(k))));

This works fine. However, I cannot add the predicates of the other Map:

strToFnc.forEach((k) => predicates.set(k, strToFnc.get(k)));
temp.ts:11:40 - error TS2345: Argument of type '(_: string) => boolean' is not assignable to parameter of type 'string'.

11 strToFnc.forEach((k) => predicates.set(k, strToFnc.get(k)));
                                          ~

temp.ts:11:56 - error TS2345: Argument of type '(_: string) => boolean' is not assignable to parameter of type 'string'.

11 strToFnc.forEach((k) => predicates.set(k, strToFnc.get(k)));
                                                          ~

Found 2 errors in the same file, starting at: temp.ts:11

Since strToFnc.get('...') returns a predicate function, why does this operation not work?


Solution

  • The forEach() method of Map objects doesn't behave the way you think it does. If you look at its typings:

    interface Map<K, V> {
      forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
    }
    

    the callback's first parameter will receive the value from the map, and its second parameter receives the key.

    Looks like you're accidentally using the value instead of the key. Assuming that you really intended to use the key, you'd need something like

    strToStr.forEach((_, k) => predicates.set(k, (_) => (k == strToStr.get(k))));
    strToFnc.forEach((_, k) => predicates.set(k, strToFnc.get(k)!));
    

    Of course you don't need get() in there since you'd also have the value:

    strToStr.forEach((v, k) => predicates.set(k, (_) => (k == v)));
    strToFnc.forEach((v, k) => predicates.set(k, v));
    

    Playground link to code