Deklarieren und initialisieren Sie ein Wörterbuch in Typescript - dictionary - Program QA

Deklarieren und initialisieren Sie ein Wörterbuch in Typescript

2013-04-08 dictionary initialization typescript

Gegeben den folgenden Code

interface IPerson {
   firstName: string;
   lastName: string;
}

var persons: { [id: string]: IPerson; } = {
   "p1": { firstName: "F1", lastName: "L1" },
   "p2": { firstName: "F2" }
};

Warum wird die Initialisierung nicht abgelehnt? Schließlich hat das zweite Objekt nicht die Eigenschaft "lastName".

Answers

Bearbeiten : Dies wurde seitdem in den neuesten TS-Versionen behoben. Zitat von @ Simon_Weavers Kommentar zum Beitrag des OP:

Hinweis: Dies wurde inzwischen behoben (nicht sicher, welche genaue TS-Version). ich Erhalten Sie diese Fehler in VS, wie Sie es erwarten würden: Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'. Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'. Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'. Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.


Anscheinend funktioniert dies nicht, wenn die ursprünglichen Daten bei der Deklaration übergeben werden. Ich denke, dies ist ein Fehler in TypeScript, daher sollten Sie einen auf der Projektsite auslösen.

Sie können das typisierte Wörterbuch verwenden, indem Sie Ihr Beispiel in Deklaration und Initialisierung aufteilen, wie z.

var persons: { [id: string] : IPerson; } = {};
persons["p1"] = { firstName: "F1", lastName: "L1" };
persons["p2"] = { firstName: "F2" }; // will result in an error

Ich stimme thomaux zu, dass der Fehler bei der Überprüfung des Initialisierungstyps ein TypeScript-Fehler ist. Ich wollte jedoch immer noch einen Weg finden, ein Wörterbuch in einer einzelnen Anweisung mit korrekter Typprüfung zu deklarieren und zu initialisieren. Diese Implementierung ist länger, fügt jedoch zusätzliche Funktionen hinzu, z. B. eine Methode mit containsKey(key: string) und remove(key: string) . Ich vermute, dass dies vereinfacht werden könnte, sobald Generika in der Version 0.9 verfügbar sind.

Zuerst deklarieren wir die Basis-Dictionary-Klasse und die Schnittstelle. Die Schnittstelle wird für den Indexer benötigt, da Klassen sie nicht implementieren können.

interface IDictionary {
    add(key: string, value: any): void;
    remove(key: string): void;
    containsKey(key: string): bool;
    keys(): string[];
    values(): any[];
}

class Dictionary {

    _keys: string[] = new string[];
    _values: any[] = new any[];

    constructor(init: { key: string; value: any; }[]) {

        for (var x = 0; x < init.length; x++) {
            this[init[x].key] = init[x].value;
            this._keys.push(init[x].key);
            this._values.push(init[x].value);
        }
    }

    add(key: string, value: any) {
        this[key] = value;
        this._keys.push(key);
        this._values.push(value);
    }

    remove(key: string) {
        var index = this._keys.indexOf(key, 0);
        this._keys.splice(index, 1);
        this._values.splice(index, 1);

        delete this[key];
    }

    keys(): string[] {
        return this._keys;
    }

    values(): any[] {
        return this._values;
    }

    containsKey(key: string) {
        if (typeof this[key] ==="undefined") {
            return false;
        }

        return true;
    }

    toLookup(): IDictionary {
        return this;
    }
}

Jetzt deklarieren wir den personenbezogenen Typ und die Dictionary / Dictionary-Schnittstelle. Beachten Sie im PersonDictionary, wie wir values() und toLookup() , um die richtigen Typen zurückzugeben.

interface IPerson {
    firstName: string;
    lastName: string;
}

interface IPersonDictionary extends IDictionary {
    [index: string]: IPerson;
    values(): IPerson[];
}

class PersonDictionary extends Dictionary {
    constructor(init: { key: string; value: IPerson; }[]) {
        super(init);
    }

    values(): IPerson[]{
        return this._values;
    }

    toLookup(): IPersonDictionary {
        return this;
    }
}

Und hier ist ein einfaches Initialisierungs- und Verwendungsbeispiel:

var persons = new PersonDictionary([
    { key: "p1", value: { firstName: "F1", lastName: "L2" } },
    { key: "p2", value: { firstName: "F2", lastName: "L2" } },
    { key: "p3", value: { firstName: "F3", lastName: "L3" } }
]).toLookup();


alert(persons["p1"].firstName + " " + persons["p1"].lastName);
// alert: F1 L2

persons.remove("p2");

if (!persons.containsKey("p2")) {
    alert("Key no longer exists");
    // alert: Key no longer exists
}

alert(persons.keys().join(", "));
// alert: p1, p3

Wenn Sie eine Eigenschaft ignorieren möchten, markieren Sie sie als optional, indem Sie ein Fragezeichen hinzufügen:

interface IPerson {
    firstName: string;
    lastName?: string;
}

Für die Verwendung eines Wörterbuchobjekts in Typoskript können Sie die folgende Schnittstelle verwenden:

interface Dictionary<T> {
    [Key: string]: T;
}

Verwenden Sie dies für Ihren Klasseneigenschaftstyp.

export class SearchParameters {
    SearchFor: Dictionary<string> = {};
}

um diese Klasse zu verwenden und zu initialisieren,

getUsers(): Observable<any> {
        var searchParams = new SearchParameters();
        searchParams.SearchFor['userId'] ='1';
        searchParams.SearchFor['userName'] ='xyz';

        return this.http.post(searchParams, 'users/search')
            .map(res => {
                return res;
            })
            .catch(this.handleError.bind(this));
    }

Hier ist eine allgemeinere Dictionary-Implementierung, die von @dmck inspiriert wurde

    interface IDictionary<T> {
      add(key: string, value: T): void;
      remove(key: string): void;
      containsKey(key: string): boolean;
      keys(): string[];
      values(): T[];
    }

    class Dictionary<T> implements IDictionary<T> {

      _keys: string[] = [];
      _values: T[] = [];

      constructor(init?: { key: string; value: T; }[]) {
        if (init) {
          for (var x = 0; x < init.length; x++) {
            this[init[x].key] = init[x].value;
            this._keys.push(init[x].key);
            this._values.push(init[x].value);
          }
        }
      }

      add(key: string, value: T) {
        this[key] = value;
        this._keys.push(key);
        this._values.push(value);
      }

      remove(key: string) {
        var index = this._keys.indexOf(key, 0);
        this._keys.splice(index, 1);
        this._values.splice(index, 1);

        delete this[key];
      }

      keys(): string[] {
        return this._keys;
      }

      values(): T[] {
        return this._values;
      }

      containsKey(key: string) {
        if (typeof this[key] ==="undefined") {
          return false;
        }

        return true;
      }

      toLookup(): IDictionary<T> {
        return this;
      }
    }

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

Diese Sammlungen sind:

  • Aufführen
  • Wörterbuch

Die Bibliothek heißt ts-generic-collection-linq .

Quellcode auf GitHub:

https://github.com/VeritasSoftware/ts-generic-collections

NPM:

https://www.npmjs.com/package/ts-generic-collections-linq

Mit dieser Bibliothek können Sie Sammlungen (wie List<T> ) erstellen und wie unten gezeigt abfragen.

    let owners = new List<Owner>();

    let owner = new Owner();
    owner.id = 1;
    owner.name ="John Doe";
    owners.add(owner);

    owner = new Owner();
    owner.id = 2;
    owner.name ="Jane Doe";
    owners.add(owner);    

    let pets = new List<Pet>();

    let pet = new Pet();
    pet.ownerId = 2;
    pet.name ="Sam";
    pet.sex = Sex.M;

    pets.add(pet);

    pet = new Pet();
    pet.ownerId = 1;
    pet.name ="Jenny";
    pet.sex = Sex.F;

    pets.add(pet);

    //query to get owners by the sex/gender of their pets
    let ownersByPetSex = owners.join(pets, owner => owner.id, pet => pet.ownerId, (x, y) => new OwnerPet(x,y))
                               .groupBy(x => [x.pet.sex])
                               .select(x =>  new OwnersByPetSex(x.groups[0], x.list.select(x => x.owner)));

    expect(ownersByPetSex.toArray().length === 2).toBeTruthy();

    expect(ownersByPetSex.toArray()[0].sex == Sex.F).toBeTruthy();
    expect(ownersByPetSex.toArray()[0].owners.length === 1).toBeTruthy();
    expect(ownersByPetSex.toArray()[0].owners.toArray()[0].name =="John Doe").toBeTruthy();

    expect(ownersByPetSex.toArray()[1].sex == Sex.M).toBeTruthy();
    expect(ownersByPetSex.toArray()[1].owners.length == 1).toBeTruthy();
    expect(ownersByPetSex.toArray()[1].owners.toArray()[0].name =="Jane Doe").toBeTruthy();

Typoskript schlägt in Ihrem Fall fehl, da erwartet wird, dass alle Felder vorhanden sind. Verwenden Sie die Dienstprogrammtypen Record und Partial, um das Problem zu lösen.

Schnittstelle IPerson { Vorname: Zeichenfolge; lastName: string; }}

var persons: Record<string, Partial<IPerson>> = {
   "p1": { firstName: "F1", lastName: "L1" },
   "p2": { firstName: "F2" }
};

Erläuterung.

  1. Der Datensatztyp erstellt ein Wörterbuch / eine Hashmap.
  2. Der Teiltyp besagt, dass einige der Felder möglicherweise fehlen.

Wechseln.

Wenn Sie den Nachnamen optional machen möchten, können Sie ein? Typoskript weiß, dass es optional ist.

lastName?: string;

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

Related