2017-07-24 6 views
1

Array von Objekten in Objekt von Objekten konvertieren Ich versuche, diese Syntax .reduce mit:Wie man richtig Funktion eingeben, die

function arrToObject<T: {key: string}, R: {[string]: T}>(list: Array<T>): R { 
    return list.reduce((result: R, item: T): R => { 
    result[item.key] = item; 
    return result; 
    }, {}); 
} 

Aber Fluss gibt den folgenden Fehler:

call of method `reduce`. Function cannot be called on any member of intersection type 

Antwort

2

Das Root-Problem ist in der Abstimmung der generischen Art R mit {}, der Anfangswert des Akkumulators.

A Hacky fix fließen würde, um zu verhindern versucht, die Arten überhaupt in Einklang zu bringen: mit dem

function arrToObject<T: {key: string}, R: {[string]: T}>(list: Array<T>): R { 
    var accum: any = {}; 
    return list.reduce((result: R, item: T): R => { 
    result[item.key] = item; 
    return result; 
    }, accum); 
} 

Aber es gibt Probleme, wie wir weiter unten heraus finden. Dinge wird ein wenig klarer, wenn wir ausdrücklich sagen, welche Art der Speicher ist:

function arrToObject<T: {key: string}, R: {[string]: T}>(list: Array<T>): R { 
    let accum: R = {}; 
    return list.reduce((result: R, item: T): R => { 
    result[item.key] = item; 
    return result; 
    }, accum); 
} 

Dies zwingt die problematische Art Versöhnung in eine einfachere Erklärung, ohne so viel Lärm um ihn herum. Und es erzeugt eine viel bessere Fehler:

3: let accum: R = {}; 
        ^object literal. This type is incompatible with 
3: let accum: R = {}; 
       ^some incompatible instantiation of `R` 

Dieser Fehler nicht die ganze Geschichte erzählen, aber es bringt uns näher an, was wirklich vor sich geht.

Flow erfordert, dass der generische Parameter Rbeliebiger Typ sein kann, der mit der Abhängigkeit kompatibel ist. Gültige Instanziierungen von R könnten daher allgemeiner sein als die Einschränkungen, die Sie ihm auferlegen, zum Beispiel mit zusätzlichen Pflichtfeldern.

Dies verursacht ein Problem. Innerhalb des Funktionskörpers können Sie möglicherweise nicht wissen, wie die tatsächliche Instanziierung von R aussieht. Du kannst also keinen konstruieren. Auch wenn die Fehlermeldungen schlecht sind, Flow ist korrekt, um Sie davon abzuhalten! Was ist, wenn jemand Ihre Funktion mit einer R Instanz angerufen hat, um etwas mit einem zusätzlichen Feld zu sein, zum Beispiel {[string]: {key: string}, selected: boolean}? Dann müsste man irgendwie wissen, dass man den Akkumulator mit einem selected: boolean Feld initialisiert.

Wenn Sie in der Lage sind, eine viel bessere Lösung als der Hack oben ist die Generika zu entfernen insgesamt:

type KeyMap = {[string]: {key: string}}; 
type Item = {key: string}; 

function arrToObject(list: Array<Item>): KeyMap { 
    return list.reduce((result:KeyMap, item: Item): KeyMap => { 
    result[item.key] = item; 
    return result; 
    }, {}); 
} 

ich auch einige Typ-Aliasnamen eingeführt, um die Funktionssignatur zu verhindern, außer Kontrolle zu geraten.

Jetzt werden die Generika durch konkrete Typen ersetzt, die Dinge sind viel einfacher. Der Rumpf der Funktion kann einen neuen Anfangswert für den Akkumulator erzeugen, da sein exakter Typ bekannt ist.

Wenn Sie aus irgendeinem Grund wirklich die generischen Typparameter benötigen, können Sie dies umgehen, indem Sie den Anfangswert des Akkumulators als weiteres Argument an arrToObject übergeben. Das funktioniert, weil der Anrufer von arrToObject den konkreten Typ kennt und instanziieren kann.

Verwandte Themen