3

I'm trying to create a Map object from a string,function dictionnary.

const entityTagDictionnary = [
  ['GRAPH_ADD_PROPERTY_SUBTYPE', (entity) => {
    console.log('addSubtype called!');
    return entity;
  }],
  ['GRAPH_IGNORE_PROPERTY_SENTENCE', (entity) => {
    console.log('ignore property called!');
    return entity;
  }],
  ['GRAPH_FORMAT_DATES', (entity) => {
    console.log('formatDates called!');
    return entity;
  }],
];

const entityMap : Map<string, Function> = new Map(entityTagDictionnary);

I've got the following error:

Argument of type '(string | ((entity: any) => any))[][]' isn't matching the argument 'Iterable<[string, Function]>'.

Am I doing anything wrong?

Baptiste Arnaud
  • 1,840
  • 3
  • 14
  • 42

2 Answers2

3

The problem is that the constructor to map takes an array of tuples and infers the type based on the tuple type. The signature for this constructor is :

new <K, V>(entries?: ReadonlyArray<[K, V]>): Map<K, V>;

The problem with your array is that is is not an array of tuples, it's an array of arrays, an item of the inner array being string | ((e: any) => any). Typescript does not infer tuple types based on array literals unless it is required to do so. The simple solution is to put the array literal in the constructor argument:

const entityMap: Map<string, Function> = new Map([
    ['GRAPH_ADD_PROPERTY_SUBTYPE', (entity: any) => {
        console.log('addSubtype called!');
        return entity;
    }],
    ['GRAPH_IGNORE_PROPERTY_SENTENCE', (entity: any) => {
        console.log('ignore property called!');
        return entity;
    }],
    ['GRAPH_FORMAT_DATES', (entity: any) => {
        console.log('formatDates called!');
        return entity;
    }],
]);

Or use an explicit type annotation:

const entityTagDictionnary: Array<[string, (e: any)=> any]> = [...]

Or you can use a tuple helper function to force typescript to infer tuple types, as described here

function tupleArray<T1, T2, T3>(arr:[T1, T2, T3][]) : typeof arr 
function tupleArray<T1, T2>(arr:[T1, T2][]) : typeof arr 
function tupleArray<T1>(arr:[T1][]) : typeof arr 
function tupleArray(arr:any[]) : any[]{
    return arr;
}
const entityTagDictionnary = tupleArray([
]);
Titian Cernicova-Dragomir
  • 157,784
  • 15
  • 245
  • 242
2

You could try the following workaround instead

const entityTagDictionnary: {[key:string]:Function} = {
   GRAPH_ADD_PROPERTY_SUBTYPE: (entity)=>{
    console.log('addSubtype called!');
    return entity;
   },
   ...
}

Then you don't need to use the new Map() call at all, and you can test it by running entityTagDictionnary['GRAPH_ADD_PROPERTY_SUBTYPE']('my entity');

John
  • 6,370
  • 4
  • 37
  • 58
  • 1
    A reasonable workaround for common situations, but there are differences between `Map` and using an object: https://stackoverflow.com/questions/18541940/map-vs-object-in-javascript – Titian Cernicova-Dragomir May 03 '18 at 10:52
  • Thank you for your comment and the link to the difference between `Map` and javascript object. – John May 03 '18 at 10:55