Objek TypeScript sebagai jenis Kamus seperti di C #

336

Saya memiliki beberapa kode JavaScript yang menggunakan objek sebagai kamus; misalnya objek 'orang' akan menyimpan beberapa detail pribadi yang dikunci dari alamat email.

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

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

Apakah mungkin untuk menggambarkan ini dalam naskah? atau apakah saya harus menggunakan Array?

Robert Taylor
sumber
5
Posting lama tetapi perhatikan bahwa ada Peta ES6
Old Badman Grey

Jawaban:

547

Dalam versi naskah yang lebih baru, Anda dapat menggunakan:

type Customers = Record<string, Customer>

Dalam versi yang lebih lama, Anda dapat menggunakan:

var map: { [email: string]: Customer; } = { };
map['[email protected]'] = new Customer(); // OK
map[14] = new Customer(); // Not OK, 14 is not a string
map['[email protected]'] = 'x'; // Not OK, 'x' is not a customer

Anda juga dapat membuat antarmuka jika Anda tidak ingin mengetikkan seluruh jenis anotasi itu setiap saat:

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

var map: StringToCustomerMap = { };
// Equivalent to first line of above
Ryan Cavanaugh
sumber
2
Itu cara yang berguna untuk memastikan bahwa kompiler membatasi indeks ke string. Menarik. Tidak terlihat seperti Anda dapat menentukan tipe indeks selain dari string atau integer, tetapi itu masuk akal, karena hanya memetakan ke indeks objek JS asli.
Ken Smith
5
Anda mungkin tahu ini, tetapi ada juga beberapa gotchas potensial dengan pendekatan ini, yang besar adalah bahwa tidak ada cara yang aman dan mudah untuk beralih melalui semua anggota. Kode ini, misalnya, menunjukkan yang mapberisi dua anggota: (<any> Object.prototype) .something = function () {}; Pelanggan kelas {} var map: {[email: string]: Customer; } = {}; map ['[email protected] '] = Pelanggan baru (); for (var i in map) {console.log (map [i])}
Ken Smith
5
bagaimana Anda menghapusnya?
TDaver
24
Pendekatan lain yang menarik adalah: interface MapStringTo <T> {[key: string]: T; } Dan deklarasikan variabel likevar map:MapStringTo<Customer> = {};
orellabac
1
Perhatikan bahwa batasan indeks tidak lagi berfungsi. Baca lebih lajut.
David Sherret
127

Selain menggunakan objek seperti peta , telah ada Mapobjek aktual untuk beberapa waktu sekarang, yang tersedia dalam TypeScript ketika dikompilasi ke ES6, atau ketika menggunakan polyfill dengan definisi tipe ES6 :

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

Ini mendukung fungsi yang sama seperti Object, dan banyak lagi, dengan sintaks yang sedikit berbeda:

// 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");

Ini sendiri memiliki beberapa keunggulan dibandingkan menggunakan map- seperti objek, seperti:

  • Dukungan untuk kunci berbasis non-string, misalnya angka atau objek, yang tidak didukung oleh keduanya Object(tidak, Objecttidak mendukung angka, itu mengubahnya menjadi string)
  • Lebih sedikit ruang untuk kesalahan saat tidak menggunakan --noImplicitAny, karena Mapselalu memiliki tipe kunci dan tipe nilai , sedangkan objek mungkin tidak memiliki indeks-tanda tangan
  • Fungsi menambahkan / menghapus item (pasangan nilai kunci) dioptimalkan untuk tugas tersebut, tidak seperti membuat properti padaObject

Selain itu, sebuah Mapobjek menyediakan API yang lebih kuat dan elegan untuk tugas-tugas umum, yang sebagian besar tidak tersedia melalui Objects sederhana tanpa meretas bersama-sama fungsi pembantu (meskipun beberapa di antaranya memerlukan ES6 iterator / polyfill iterable penuh untuk target ES5 atau di bawah):

// 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());
John Weisz
sumber
2
Itu luar biasa! Tapi sayangnya itu salah menggunakan serial JSON.stringify(), sehingga dapat digunakan misalnya untuk socket.io :(
Lion
@ Singa - yah, Mapserialisasi agak lucu. Saya, untuk satu, melakukan konversi ke objek-pasangan kunci-nilai sebelum membuat serial, dan kemudian kembali (misalnya objek { key: "John", value: { firstName: "John" } }).
John Weisz
2
Saya membuat kesalahan dengan menggunakan peta, bukan objek tua biasa, dan serialisasi benar-benar membuat saya. Lebih jelas menurut saya.
user378380
1
Ini indah. Senang sekali Anda mengilhami saya untuk akhirnya masuk ke peta. Ini akan cukup banyak menggantikan struktur keymap / kamus saya yang biasa karena sangat mudah untuk mengetikkan kunci.
Metodologi
77

Anda dapat menggunakan antarmuka templated seperti ini:

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

let dict: Map<number> = {};
dict["one"] = 1;
Dimitar Mazhlekov
sumber
7
Perhatikan bahwa ini bertabrakan dengan tipe Peta ES6. Lebih baik daripada jawaban yang lain karena batasan indeks diabaikan.
Old Badman Grey
bagaimana cara memeriksa apakah ada kunci dalam kamus?
samneric
dict.hasOwnProperty ('key')
Dimitar Mazhlekov
1
Saya menggunakan Kamus alih-alih Peta untuk menghindari kebingungan, dan Anda dapat menggunakan notasi objek literal:let dict: Dictionary<number> = { "one": 1, "two": 2 };
PhiLho
8

Anda juga dapat menggunakan tipe Catatan dalam naskah:

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

Lodash memiliki implementasi Kamus sederhana dan memiliki dukungan TypeScript yang baik

Instal Lodash:

npm install lodash @types/lodash --save

Impor dan penggunaan:

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

Anda dapat menggunakan Recordini:

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

Contoh (Pemetaan antara AppointmentStatus enum dan beberapa data meta):

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

Sekarang dengan antarmuka sebagai nilai:

interface Icon { Name: string Color: string }

Pemakaian:

const icon: SemanticIcon = iconMapping[appointment.Status]

Nick N.
sumber
Ini sangat berguna. Apakah Anda menggunakan string enumatau class/objectuntuk AppointmentStatus- atau apakah itu penting?
Drenai
@Renai tidak masalah, itu yang Anda sukai
Nick N.
-2

Ada perpustakaan yang menyediakan koleksi yang sangat diketik, queryable dalam naskah.

Koleksinya adalah:

  • Daftar
  • Kamus

Perpustakaan disebut koleksi-ts-generik . ( Kode sumber di GitHub )

Anda dapat membuat kamus dan menanyakannya seperti di bawah ini:

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