Deklarasikan dan inisialisasi Kamus dalam Teks

248

Diberikan kode berikut

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

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

Mengapa inisialisasi tidak ditolak? Lagi pula, objek kedua tidak memiliki properti "lastName".

mgs
sumber
11
Catatan: ini telah diperbaiki (tidak yakin versi TS mana yang tepat). Saya mendapatkan kesalahan ini dalam VS, seperti yang Anda harapkan: Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.
Simon_Weaver

Jawaban:

289

Sunting : Ini telah diperbaiki di versi TS terbaru. Mengutip komentar @ Simon_Weaver di pos OP:

Catatan: ini telah diperbaiki (tidak yakin versi TS mana yang tepat). Saya mendapatkan kesalahan ini dalam VS, seperti yang Anda harapkan:Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.


Tampaknya ini tidak berfungsi saat meneruskan data awal saat deklarasi. Saya kira ini adalah bug dalam TypeScript, jadi Anda harus membuatnya di situs proyek.

Anda dapat menggunakan kamus yang diketik dengan memisahkan contoh Anda dalam deklarasi dan inisialisasi, seperti:

var persons: { [id: string] : IPerson; } = {};
persons["p1"] = { firstName: "F1", lastName: "L1" };
persons["p2"] = { firstName: "F2" }; // will result in an error
thomaux
sumber
3
Mengapa Anda membutuhkan idsimbol? Sepertinya itu tidak perlu.
Kiewic
4
Dengan menggunakan idsimbol, Anda dapat mendeklarasikan apa jenis kunci kamus seharusnya. Dengan deklarasi di atas, Anda tidak dapat melakukan hal berikut:persons[1] = { firstName: 'F1', lastName: 'L1' }
thomaux
2
Selalu lupakan sintaks ini karena alasan tertentu!
eddiewould
12
yang idsimbol dapat diberi nama apa pun yang Anda suka dan dirancang seperti itu untuk membuatnya lebih mudah untuk membaca kode. mis { [username: string] : IPerson; }
Guy Park
1
@Robouste Saya akan menggunakan metode findKey Lodash atau jika Anda lebih suka solusi asli, Anda bisa membangun di Object.entries . Jika Anda tertarik untuk mendapatkan daftar kunci yang lengkap, lihat Object.keys
thomaux
82

Untuk menggunakan objek kamus dalam naskah Anda dapat menggunakan antarmuka seperti di bawah ini:

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

dan, gunakan ini untuk tipe properti kelas Anda.

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

untuk menggunakan dan menginisialisasi kelas ini,

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

Saya setuju dengan thomaux bahwa kesalahan pengecekan tipe inisialisasi adalah bug TypeScript. Namun, saya masih ingin menemukan cara untuk mendeklarasikan dan menginisialisasi Kamus dalam satu pernyataan dengan pemeriksaan tipe yang benar. Implementasi ini lebih lama, namun menambahkan fungsionalitas tambahan seperti a containsKey(key: string)dan remove(key: string)metode. Saya menduga ini bisa disederhanakan begitu obat generik tersedia di rilis 0.9.

Pertama kita mendeklarasikan kelas Kamus dasar dan Antarmuka. Antarmuka diperlukan untuk pengindeks karena kelas tidak dapat mengimplementasikannya.

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

Sekarang kita mendeklarasikan tipe khusus Orang dan antarmuka Kamus / Kamus. Dalam catatan PersonDictionary bagaimana kita menimpa values()dan toLookup()mengembalikan tipe yang benar.

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

Dan ini adalah contoh inisialisasi dan penggunaan yang sederhana:

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
dmck
sumber
Kode sampel sangat membantu. "Antarmuka IDictionary" berisi kesalahan ketik kecil, karena ada referensi ke IPerson.
mgs
akan lebih baik untuk menerapkan jumlah elemen juga
nurettin
@ dmck Deklarasi containsKey(key: string): bool;ini tidak berfungsi dengan TypeScript 1.5.0-beta . Itu harus diubah menjadi containsKey(key: string): boolean;.
Amarjeet Singh
1
mengapa Anda tidak menghapus tipe generik? Kamus <T>, maka tidak perlu membuat kelas PersonDictionary. Anda mendeklarasikannya seperti ini: var persons = Kamus baru <IPerson> ();
Benoit
1
Saya telah menggunakan kamus umum seperti itu secara efektif. Saya menemukannya di sini: fabiolandoni.ch/...
CAK2
5

Berikut ini adalah implementasi Kamus yang lebih umum yang terinspirasi oleh ini dari @ 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;
      }
    }
mbcom
sumber
3

Jika Anda ingin mengabaikan properti, tandai sebagai opsional dengan menambahkan tanda tanya:

interface IPerson {
    firstName: string;
    lastName?: string;
}
Massimiliano Kraus
sumber
1
Inti dari pertanyaan adalah mengapa kode yang diberikan dikompilasi tanpa memberikan nama belakang ...
Pierre Arlaud
-1

Sekarang, ada perpustakaan yang menyediakan koleksi yang sangat diketik, queryable dalam naskah.

Koleksi-koleksi ini adalah:

  • Daftar
  • Kamus

Perpustakaan disebut ts-generic-collections-linq .

Kode sumber di GitHub:

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

NPM:

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

Dengan perpustakaan ini, Anda dapat membuat koleksi (seperti List<T>) dan meminta mereka seperti yang ditunjukkan di bawah ini.

    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();
John
sumber
tidak dapat menemukan paket npm untuk ini
Harry
1
@ Harry - paket npm disebut "ts-generic-collections-linq"
Ade