2016-05-05 7 views
3

Ich immutable mapTyposkript. Wie erhält man den Objektnamen eines Objekts von seinem Wert?

class NavigableObject<T> { 
    constructor(private obj: T, private path: string[] = []) { } 

    To<R>(p: (x: T) => R): NavigableObject<R> { 
     return new NavigableObject<R>(p(this.obj), 
         this.path.concat(this.getPropName(p(this.obj)))); 
    } 

    getPath() { 
     return this.path; 
    } 

    private getPropName(value) { 
     for (var item in this.obj) { 
      if (this.obj[item] === value) { 
       return item; 
      } 
     } 
    } 
} 

let test = { 
    a: { 
     a1: 1, 
     a2: 1 
    }, 
    b: { 
     b1: 1, 
     b2: 2 
    } 
} 

let navigableTest = new NavigableObject(test); 

navigableTest.To(m => m.b).To(m => m.b2).getPath(); // = ["b", "b2"] 

navigableTest.To(m => m.a).To(m => m.a2).getPath(); // = ["a", "a1"] <-- I selected a2, tho 

mit einer Typoskript Klasse Schreiben für die Verwendung Es gibt ein Problem mit getPropName Methode. Wenn obj zwei Eigenschaften mit demselben Wert hat, wird nur die erste Eigenschaft verglichen.

Weiß jemand, wie man das umgeht?

+1

Was wollen Sie passieren? Eine Reihe von Schlüsseln? Ein Fehler wegen Nicht-Eindeutigkeit? Was? –

+0

Die Schlüssel eines Objekts sollten eindeutig sein. Können Sie ein Beispiel dafür geben, wie/warum Sie ein Objekt mit mehr als einem Schlüssel mit demselben Namen haben würden? –

+0

Ich meine, ein Objekt hat zwei Eigenschaften mit dem gleichen Wert (nicht der gleiche Schlüssel). –

Antwort

3

könnten Sie this way of getting the property name verwenden:

class NavigableObject<T> { 
    constructor(private obj: T, private path: string[] = []) { } 

    To<R>(p: (x: T) => R): NavigableObject<R> { 
     return new NavigableObject<R>(p(this.obj), 
         this.path.concat(this.getPropName(p))); 
    } 

    getPath() { 
     return this.path; 
    } 

    private static propertyRegEx = /\.([^\.;]+);?\s*\}$/; 

    private getPropName(propertyFunction: Function) { 
     return NavigableObject.propertyRegEx.exec(propertyFunction.toString())[1]; 
    } 
} 

let test = { 
    a: { 
     a1: 1, 
     a2: 1 
    }, 
    b: { 
     b1: 1, 
     b2: 2 
    } 
} 

let navigableTest = new NavigableObject(test); 

navigableTest.To(m => m.b).To(m => m.b2).getPath(); // = ["b", "b2"] 

navigableTest.To(m => m.a).To(m => m.a2).getPath(); // = ["a", "a2"] 
2

Es gibt nichts Einfaches, was Sie hier mit Ihrem aktuellen Abfrageansatz tun können. Es ist mehrdeutig, welche Eigenschaft du auswählst, also wird es keinen einfachen Weg geben, den richtigen Weg zu finden. Es ist nicht so, dass dein Code jetzt als solcher "falsch" ist, es gibt nur zwei mögliche richtige Antworten und es wird willkürlich gewählt.

Sie können die Regeln für die möglichen Schlüssel ändern, aber es gibt keine vernünftigen Regeln, die Ihnen zuverlässig die richtige Antwort geben. Alternativ könnten Sie alle möglichen Antworten zurückgeben und einen mehrdeutigen Pfad haben, aber es scheint nicht so zu sein, wonach Sie suchen.

Es könnte eine Möglichkeit geben, verrückte Dinge zu tun, wie das Parsen der Funktion Esprima oder sogar reguläre Ausdrücke, um herauszufinden, welche Eigenschaften erfasst werden, aber es wird im Allgemeinen eine schlechte Idee sein. Das ist wahrscheinlich komplex und zerbrechlich, unzuverlässig, es sei denn, Sie können die genaue Form des Codes, den Sie in To() erhalten, garantieren und auch ziemlich langsam laufen.

Wenn Sie in der Lage sein möchten, Eigenschaften wie diese zu wählen und den Pfad für bestimmte verwendet wissen, müssen Sie Ihre To Funktion den Schlüssel der Eigenschaft geben, nicht nur eine Funktion, um den Wert zu erhalten. Es ist eine weniger elegante API, aber es ist die einzige vernünftige Art und Weise Sie das Verhalten Sie suchen bekommen sind:

class NavigableObject<T> { 
    constructor(private obj: T, private path: string[] = []) { } 

    To<R>(p: string): NavigableObject<R> { 
     return new NavigableObject<R>(this.obj[p], 
         this.path.concat(p)); 
    } 

    getPath() { 
     return this.path; 
    } 
} 

let test = { 
    a: { 
     a1: 1, 
     a2: 1 
    }, 
    b: { 
     b1: 1, 
     b2: 2 
    } 
} 

let navigableTest = new NavigableObject(test); 

navigableTest.To('b').To('b2').getPath(); 

navigableTest.To('a').To('a2').getPath(); 

In Zukunft möglich sein, könnte dies mit Typ-Sicherheit in Typoskript zu tun, aber nicht jetzt sofort. This PR befasst sich genau mit diesen Problemen, aber es wird immer noch diskutiert, so dass es eine Weile dauern wird, bis es implementiert ist. Beachten Sie, dass es immer noch den String-basierten Ansatz aus dem obigen Beispiel vorschlägt, es ist nur das Typsystem, das überprüft, dass String-Konstanten gültige Eigenschaftsnamen für die verwendeten Typen sind.

0

Beenden Sie nicht den Wert für den ersten gefundenen Wert und geben Sie ein Array mit Namen mit übereinstimmenden Werten zurück. Ich überlasse Ihnen das Problem, wie Sie mit mehreren Namen umgehen.

private getPropName (value) { 
    var items = []; 
    for (var item in this.obj) { 
     if (this.obj[item] === value) 
      items.push (item); 
    return items; 
} 
1

Die Zukunft ist hier. Laut Tim Perrys link hat TypeScript jetzt keyof hinzugefügt, was eine großartige Funktion ist, um die verfügbaren Eigenschaften einer Klasse zu erhalten.

Verwendung, gemäß dem Typoskript documentation:

interface Person { 
    name: string; 
    age: number; 
    location: string; 
} 

type K1 = keyof Person; // "name" | "age" | "location" 
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ... 
type K3 = keyof { [x: string]: Person }; // string 
Verwandte Themen