2016-12-21 1 views
0

Der folgende Code wird in Typoskript 2.1.4 geben den Fehler nicht kompilieren:TypeScript kompiliert nicht, wenn Ausdruck als Eigenschaftenname für die Access-Funktion mit einem Argument verwendet wird, dessen Typ optionale Felder enthält. TS2349

Fehler

Error:(6, 31) TS2349:Cannot invoke an expression whose type lacks a call signature. Type '((args: string[], action: A) => string[]) | ((args: string[], action: C) => string[])' has no compatible call signatures.

-Code

/* 
* Set up a function to take some arguments 
* and an action and use the map to run the appropriate 
* function bases on the type property of the action 
*/ 

const caller = (args: string[] = [], action): string[] => { 
     return map[action.type] ? map[action.type](args, action) : args; 
}; 

interface Action { 
    type: any; 
} 

const TYPE_A = "type_a"; 

interface A extends Action { 
    from: number; 
    to: number; 
    id?: number; // optional parameters causing the issue. 
    prop1?: number; 
} 

const TYPE_B = "type_b"; 

interface B extends Action { 
    from: number; 
    to: number; 
} 

const TYPE_C = "type_c"; 

interface C extends Action { 
    id: number; 
    prop1: number; 
} 

const map = { 
    [TYPE_A]: (args: string[], action: A) => { 
     return ["a"]; 
    }, 
    [TYPE_B]: (args: string[], action: B) => { 
     return ["b"]; 
    }, 
    [TYPE_C]: (args: string[], action: C) => { 
     return ["c"]; 
    } 
}; 

caller([], {type: TYPE_A, from: 2, to: 1}); 

Motivation

Meine Motivation, einen Ausdruck als Eigenschaft in der Map zu verwenden, ist so, dass ich den Wert der Eigenschaftskonstanten ändern kann, ohne die Map neu zu gestalten.

Lösungen

Es gibt zwei Möglichkeiten, dies zu lösen:

a) die optionalen Felder entfernen in interface A.

interface A extends Action { 
    from: number; 
    to: number; 
    id: number; // optional parameters causing the issue not optional. 
    prop1: number; 
} 

b) die Erklärungen Mapeigenschaften auf Werte ändern und nicht die Ausdrücke und optionale Felder halten.

const map = { 
    "type_a" : (args: string[], action: A) => { 
     return ["a"]; 
    }, 
    "type_b": (args: string[], action: B) => { 
     return ["b"]; 
    }, 
    "type_c": (args: string[], action: C) => { 
     return ["c"]; 
    } 
}; 

Frage

ist meine Frage, warum der Fehler in erster Linie gezeigt, kann jemand mir dies erklären?

Antwort

0

Der Grund dafür ist, dass A und C unvereinbar sind, weil prop1 in A und benötigt in C optional. So können Sie nicht Funktion verwenden, die C in stattfindet, wo eine Funktion, die A nimmt gebraucht wird:

let fa: (a: A) => void; 
let fc: (c: C) => void; 

fa = fc; 
fc = fa; 

Fehler:

test.ts(49,1): error TS2322: Type '(c: C) => void' is not assignable to type '(a: A) => void'. 
    Types of parameters 'c' and 'a' are incompatible. 
    Type 'A' is not assignable to type 'C'. 
     Property 'id' is optional in type 'A' but required in type 'C'. 
test.ts(50,1): error TS2322: Type '(a: A) => void' is not assignable to type '(c: C) => void'. 
    Types of parameters 'a' and 'c' are incompatible. 
    Type 'C' is not assignable to type 'A'. 
     Property 'from' is missing in type 'C'. 

Wenn Sie eine Karte mit wörtlichen Eigenschaftsnamen deklarieren, Typinferenz können herauszufinden, wenn Sie caller([], {type: TYPE_A, from: 2, to: 1}); tun, greifen Sie tatsächlich auf einen Wert mit "type_a" Schlüssel, so dass es weiß, dass die Funktion Parametertyp genau A ist. Wenn Map mit berechneten Schlüsseln deklariert wird, kann es das wahrscheinlich nicht tun, da es Ausdrücke für Schlüssel zur Kompilierungszeit einfach nicht auswertet, so dass es einen Union-Typen für Kartenwerte ableitet, und zwei Mitglieder der Union sind miteinander inkompatibel weil A und C sind nicht kompatibel.

können Sie auch nur dieses Problem umgehen, indem sie ausdrücklich Typ für map erklärt:

const map: {[key: string]: (args:string[], action: Action) => string[]} = { 
    [TYPE_A]: (args: string[], action: A) => { 
     return ["a"]; 
    }, 
    [TYPE_B]: (args: string[], action: B) => { 
     return ["b"]; 
    }, 
    [TYPE_C]: (args: string[], action: C) => { 
     return ["c"]; 
    } 
}; 

auch funktioniert.

Verwandte Themen