web-dev-qa-db-fra.com

Déclarer et initialiser un dictionnaire en dactylographie

Étant donné le code suivant

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

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

Pourquoi l'initialisation n'est-elle pas rejetée? Après tout, le deuxième objet n'a pas la propriété "lastName".

164
mgs

Edit : Cela a depuis été corrigé dans les dernières versions de TS. Citant le commentaire de @ Simon_Weaver sur le message de l'OP:

Remarque: cela a été corrigé depuis (je ne sais pas quelle version exacte du TS). JE obtenez ces erreurs dans VS, comme vous vous en doutez: Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.


Apparemment, cela ne fonctionne pas lors de la transmission des données initiales à la déclaration . Je suppose que c'est un bogue dans TypeScript, vous devriez donc en créer un sur le site du projet.

Vous pouvez utiliser le dictionnaire typé en séparant votre exemple en déclaration et initialisation, comme suit:

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

Je suis d'accord avec thomaux sur le fait que l'erreur de vérification du type d'initialisation est un bogue TypeScript. Cependant, je voulais toujours trouver un moyen de déclarer et d’initialiser un dictionnaire dans une seule instruction avec une vérification de type correcte. Cette implémentation est plus longue, mais elle ajoute des fonctionnalités supplémentaires telles que les méthodes containsKey(key: string) et remove(key: string). Je soupçonne que cela pourrait être simplifié une fois que les génériques seront disponibles dans la version 0.9.

Nous déclarons d’abord la classe de base Dictionary et l’interface. L'interface est requise pour l'indexeur car les classes ne peuvent pas les implémenter.

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;
    }
}

Nous déclarons maintenant le type spécifique à la personne et l'interface Dictionnaire/Dictionnaire. Dans PersonDictionary, notez comment nous substituons values() et toLookup() pour renvoyer les types corrects.

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;
    }
}

Et voici un exemple simple d'initialisation et d'utilisation:

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
53
dmck

Pour utiliser un objet dictionnaire dans TypeScript, vous pouvez utiliser l'interface comme ci-dessous:

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

et, utilisez ceci pour votre type de propriété de classe.

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

utiliser et initialiser cette classe,

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));
    }
45
Amol Bhor

Si vous souhaitez ignorer une propriété, marquez-la comme facultative en ajoutant un point d'interrogation:

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

Voici une implémentation plus générale du dictionnaire inspirée par ceci de @dmck 

    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;
      }
    }
1
mbcom

À présent, il existe une bibliothèque qui fournit des collections interrogeables fortement typées dans TypeScript.

Les collections sont:

  • Liste
  • Dictionnaire

La bibliothèque s'appelle ts-generic-collections .

Code source sur GitHub:

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

Avec cette bibliothèque, vous pouvez créer des collections (comme List<T>) et les interroger comme indiqué ci-dessous.

    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();
0
John