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".


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

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

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

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

3

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

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

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

tidak dapat menemukan paket npm untuk ini
Harry

1
@ Harry - paket npm disebut "ts-generic-collections-linq"
Ade
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.