TypeScript-Objekte als Wörterbuchtypen wie in C #


335

Ich habe JavaScript-Code, der Objekte als Wörterbücher verwendet. Beispielsweise enthält ein "Personen" -Objekt einige persönliche Daten, die von der E-Mail-Adresse eingegeben wurden.

var people = {<email> : <'some personal data'>};

adding   > "people[<email>] = <data>;" 
getting  > "var data = people[<email>];" 
deleting > "delete people[<email>];"

Ist es möglich, dies in Typescript zu beschreiben? oder muss ich ein Array verwenden?


5
Alter Beitrag, aber beachten Sie, dass es die ES6 Map
Old Badman Grey

Antworten:


544

In neueren Versionen von Typoskript können Sie Folgendes verwenden:

type Customers = Record<string, Customer>

In älteren Versionen können Sie verwenden:

var map: { [email: string]: Customer; } = { };
map['foo@gmail.com'] = new Customer(); // OK
map[14] = new Customer(); // Not OK, 14 is not a string
map['bar@hotmail.com'] = 'x'; // Not OK, 'x' is not a customer

Sie können auch eine Schnittstelle erstellen, wenn Sie nicht jedes Mal die gesamte Typanmerkung eingeben möchten:

interface StringToCustomerMap {
    [email: string]: Customer;
}

var map: StringToCustomerMap = { };
// Equivalent to first line of above

2
Dies ist eine nützliche Methode, um sicherzustellen, dass der Compiler Indizes auf Zeichenfolgen beschränkt. Interessant. Es sieht nicht so aus, als könnten Sie den Indextyp als etwas anderes als Zeichenfolgen oder Ganzzahlen angeben, aber das ist sinnvoll, da er nur den nativen JS-Objektindizes zugeordnet ist.
Ken Smith

5
Sie wissen das vielleicht, aber es gibt auch einige potenzielle Fallstricke bei diesem Ansatz. Das große Problem ist, dass es keinen sicheren und einfachen Weg gibt, alle Mitglieder zu durchlaufen. Dieser Code zeigt zum Beispiel, dass er mapzwei Mitglieder enthält: (<any> Object.prototype) .something = function () {}; Klasse Kunde {} var map: {[email: string]: Kunde; } = {}; map ['foo@gmail.com '] = neuer Kunde (); für (var i in map) {console.log (map [i])}
Ken Smith

5
Wie entfernst du es?
TDaver

24
Ein weiterer interessanter Ansatz ist: interface MapStringTo <T> {[key: string]: T; } Und die deklarieren die Variable wievar map:MapStringTo<Customer> = {};
orellabac

1
Beachten Sie, dass die Indexbeschränkung nicht mehr funktioniert. Weiterlesen.
David Sherret

127

Zusätzlich zur Bereitstellung einer map- zu verwenden wie Objekt, ein tatsächliches war MapGegenstand einiger Zeit, die in Typoskript zur Verfügung steht , wenn sie ES6 kompilieren oder wenn eine polyfill mit den ES6 mit Typ-Definitionen :

let people = new Map<string, Person>();

Es unterstützt die gleiche Funktionalität Objectund mehr mit einer etwas anderen Syntax:

// Adding an item (a key-value pair):
people.set("John", { firstName: "John", lastName: "Doe" });

// Checking for the presence of a key:
people.has("John"); // true

// Retrieving a value by a key:
people.get("John").lastName; // "Doe"

// Deleting an item by a key:
people.delete("John");

Dies allein hat mehrere Vorteile gegenüber einer map- mit wie Objekt, wie zum Beispiel:

  • Unterstützung für nicht auf Zeichenfolgen basierende Schlüssel, z. B. Zahlen oder Objekte, von denen Objectkeine unterstützt wird (nein, Objectunterstützt keine Zahlen, konvertiert sie in Zeichenfolgen)
  • Weniger Spielraum für Fehler bei Nichtverwendung --noImplicitAny, da a Mapimmer einen Schlüsseltyp und einen Werttyp hat , während ein Objekt möglicherweise keine Indexsignatur hat
  • Die Funktionalität zum Hinzufügen / Entfernen von Elementen (Schlüssel-Wert-Paare) ist für die Aufgabe optimiert, im Gegensatz zum Erstellen von Eigenschaften auf einemObject

Darüber hinaus bietet ein MapObjekt eine leistungsfähigere und elegantere API für allgemeine Aufgaben, von denen die meisten nicht über einfache Objects verfügbar sind, ohne die Hilfsfunktionen zu hacken (obwohl einige davon einen vollständigen ES6-Iterator / eine iterierbare Polyfüllung für ES5-Ziele oder weniger erfordern):

// Iterate over Map entries:
people.forEach((person, key) => ...);

// Clear the Map:
people.clear();

// Get Map size:
people.size;

// Extract keys into array (in insertion order):
let keys = Array.from(people.keys());

// Extract values into array (in insertion order):
let values = Array.from(people.values());

2
Das ist großartig! Aber leider wurde es falsch serialisiert mit JSON.stringify(), so dass es zB für socket.io verwendet werden kann :(
Lion

@ Lion - na ja, MapSerialisierung ist ziemlich lustig. Zum einen führe ich vor der Serialisierung eine Konvertierung in Schlüssel-Wert-Paar-Objekte durch und dann zurück (z { key: "John", value: { firstName: "John" } }. B. Objekt von ).
John Weisz

2
Ich habe den Fehler gemacht, eine Karte anstelle eines einfachen alten Objekts zu verwenden, und die Serialisierung hat mich wirklich erwischt. Meiner Meinung nach klar steuern.
user378380

1
Dies ist schön. Ich bin so froh, dass du mich dazu inspiriert hast, endlich in Karten einzutauchen. Dies wird meine üblichen Keymap- / Wörterbuchstrukturen so ziemlich ersetzen, da es so viel einfacher ist, die Tasten stark einzugeben.
Methodician

77

Sie können folgende Vorlagenschnittstellen verwenden:

interface Map<T> {
    [K: string]: T;
}

let dict: Map<number> = {};
dict["one"] = 1;

7
Beachten Sie, dass dies mit dem Map-Typ es6 kollidiert. Besser als die andere Antwort, da die Indexbeschränkung ignoriert wird.
Alter Badman Gray

Wie überprüfe ich, ob ein Schlüssel im Wörterbuch vorhanden ist?
Samneric

dict.hasOwnProperty ('Schlüssel')
Dimitar Mazhlekov

1
Ich benutze Dictionary anstelle von Map, um Verwirrung zu vermeiden, und Sie können die wörtliche let dict: Dictionary<number> = { "one": 1, "two": 2 };
Objektnotation

8

Sie können den Datensatztyp auch in Typoskript verwenden:

export interface nameInterface { 
    propName : Record<string, otherComplexInterface> 
}

5

Lodash hat eine einfache Dictionary-Implementierung und eine gute TypeScript-Unterstützung

Installieren Sie Lodash:

npm install lodash @types/lodash --save

Import und Verwendung:

import { Dictionary } from "lodash";
let properties : Dictionary<string> = {
    "key": "value"        
}
console.log(properties["key"])

3

Sie können dafür verwenden Record:

https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkt

Beispiel (Eine Zuordnung zwischen AppointmentStatus-Enum und einigen Metadaten):

  const iconMapping: Record<AppointmentStatus, Icon> = {
    [AppointmentStatus.Failed]: { Name: 'calendar times', Color: 'red' },
    [AppointmentStatus.Canceled]: { Name: 'calendar times outline', Color: 'red' },
    [AppointmentStatus.Confirmed]: { Name: 'calendar check outline', Color: 'green' },
    [AppointmentStatus.Requested]: { Name: 'calendar alternate outline', Color: 'orange' },
    [AppointmentStatus.None]: { Name: 'calendar outline', Color: 'blue' }
  }

Jetzt mit Schnittstelle als Wert:

interface Icon { Name: string Color: string }

Verwendungszweck:

const icon: SemanticIcon = iconMapping[appointment.Status]


Das ist sehr nützlich. Würden Sie einen String enumoder ein class/objectfor verwenden AppointmentStatus- oder spielt es eine Rolle?
Drenai

Für mich ist es eine Aufzählung. Siehe meine aktualisierte Antwort.
Nick N.

@ Drenai spielt keine Rolle, es ist das, was Sie bevorzugen
Nick N.

-2

Es gibt eine Bibliothek, die stark typisierte, abfragbare Sammlungen in Typoskript bereitstellt .

Die Sammlungen sind:

  • Liste
  • Wörterbuch

Die Bibliothek heißt ts-generic-collection . ( Quellcode auf GitHub )

Sie können ein Wörterbuch erstellen und wie folgt abfragen:

  it('firstOrDefault', () => {
    let dictionary = new Dictionary<Car, IList<Feature>>();

    let car = new Car(1, "Mercedez", "S 400", Country.Germany);
    let car2 = new Car(2, "Mercedez", "S 500", Country.Germany);

    let features = new List<Feature>();

    let feature = new Feature(1, "2 - Door Sedan");
    features.add(feature);

    dictionary.add(car, features);

    features = new List<Feature>();
    feature = new Feature(2, "4 - Door Sedan");
    features.add(feature);

    dictionary.add(car2, features);

    //query
    let first = dictionary.firstOrDefault(x => x.key.name == "Mercedez");

    expect(first.key.id = 1);
  });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.