Đối tượng TypeScript như các loại từ điển như trong C #


335

Tôi có một số mã JavaScript sử dụng các đối tượng làm từ điển; ví dụ, một đối tượng 'người' sẽ giữ một số chi tiết cá nhân được khóa trong địa chỉ email.

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

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

Có thể mô tả điều này trong Bản in? hoặc tôi phải sử dụng một mảng?


5
Bài cũ nhưng lưu ý rằng có Bản đồ ES6
Old Badman Grey

Câu trả lời:


544

Trong các phiên bản mới hơn của bản thảo, bạn có thể sử dụng:

type Customers = Record<string, Customer>

Trong các phiên bản cũ hơn, bạn có thể sử dụng:

var map: { [email: string]: Customer; } = { };
map['foo@gmail.com'] = new Customer(); // OK
map[14] = new Customer(); // Not OK, 14 is not a string
map['bar@hotmail.com'] = 'x'; // Not OK, 'x' is not a customer

Bạn cũng có thể tạo giao diện nếu bạn không muốn nhập toàn bộ chú thích loại đó mỗi lần:

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

var map: StringToCustomerMap = { };
// Equivalent to first line of above

2
Đó là một cách hữu ích để đảm bảo rằng trình biên dịch hạn chế các chỉ mục thành chuỗi. Hấp dẫn. Không có vẻ như bạn có thể chỉ định loại chỉ mục là bất kỳ thứ gì ngoài chuỗi hoặc số nguyên, nhưng điều đó có ý nghĩa, vì nó chỉ ánh xạ tới các chỉ mục đối tượng JS gốc.
Ken Smith

5
Bạn có thể biết điều này, nhưng cũng có một số vấn đề tiềm năng với cách tiếp cận này, vấn đề lớn là không có cách nào an toàn và dễ dàng để lặp qua tất cả các thành viên. Ví dụ, mã này cho thấy mapcó chứa hai thành viên: (<any> Object.prototype) .s Something = function () {}; lớp Khách hàng {} var map: {[email: chuỗi]: Khách hàng; } = {}; map ['foo @ gmail '] = Khách hàng mới (); for (var i in map) {console.log (map [i])}
Ken Smith

5
Làm thế nào để bạn loại bỏ nó?
TDaver

24
Một cách tiếp cận thú vị khác là: giao diện MapStringTo <T> {[key: string]: T; } Và khai báo biến nhưvar map:MapStringTo<Customer> = {};
orellabac

1
Hãy lưu ý rằng các ràng buộc chỉ mục không còn hoạt động. Đọc thêm.
David Sherret

127

Ngoài việc sử dụng một đối tượng giống như bản đồ , hiện tại đã có một Mapđối tượng thực tế , hiện có sẵn trong TypeScript khi biên dịch sang ES6 hoặc khi sử dụng một polyfill với định nghĩa kiểu ES6 :

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

Nó hỗ trợ các chức năng tương tự như Object, và hơn thế nữa, với cú pháp hơi khác:

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

Điều này một mình có một số lợi thế so với việc sử dụng một đối tượng giống như bản đồ , chẳng hạn như:

  • Hỗ trợ cho các khóa không dựa trên chuỗi, ví dụ như số hoặc đối tượng, không có khóa nào được hỗ trợ bởi Object(không, Objectkhông hỗ trợ số, nó chuyển đổi chúng thành chuỗi)
  • Ít chỗ hơn cho các lỗi khi không sử dụng --noImplicitAny, vì Mapluôn có loại khóa và loại giá trị , trong khi một đối tượng có thể không có chữ ký chỉ mục
  • Chức năng thêm / xóa các mục (cặp giá trị khóa) được tối ưu hóa cho tác vụ, không giống như tạo các thuộc tính trên mộtObject

Ngoài ra, một Mapđối tượng cung cấp API mạnh mẽ và thanh lịch hơn cho các tác vụ thông thường, hầu hết không có sẵn thông qua các đơn giản Objectmà không hack cùng các chức năng của trình trợ giúp (mặc dù một số trong số này yêu cầu polyfill / lặp lặp ES6 đầy đủ cho các mục tiêu ES5 trở xuống):

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

2
Thật tuyệt vời! Nhưng thật đáng buồn là nó đã bị đăng tuần tự sai JSON.stringify(), vì vậy nó có thể được sử dụng, ví dụ như cho socket.io :(
Lion

@Lion - vâng, Mapserialization khá buồn cười. Tôi, trước hết, thực hiện chuyển đổi thành các đối tượng cặp khóa-giá trị trước khi tuần tự hóa, rồi quay lại (ví dụ: đối tượng của { key: "John", value: { firstName: "John" } }).
John Weisz

2
Tôi đã phạm sai lầm khi sử dụng bản đồ thay vì một vật thể cũ đơn giản và việc xê-ri hóa thực sự đã giúp tôi. Chỉ đạo rõ ràng trong quan điểm của tôi.
dùng378380

1
Thật là đẹp Rất vui vì bạn đã truyền cảm hứng cho tôi để cuối cùng nhúng vào bản đồ. Điều này sẽ thay thế khá nhiều cấu trúc từ điển / sơ đồ thông thường của tôi vì việc gõ phím dễ dàng hơn nhiều.
phương pháp

77

Bạn có thể sử dụng giao diện templated như thế này:

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

let dict: Map<number> = {};
dict["one"] = 1;

7
Lưu ý rằng điều này va chạm với loại Bản đồ es6. Tốt hơn so với câu trả lời khác vì ràng buộc chỉ số bị bỏ qua.
Old Badman Grey

Làm cách nào để kiểm tra nếu một khóa tồn tại trong từ điển?
samneric

dict.hasOwnProperty ('key')
Dimitar Mazhlekov

1
Tôi sử dụng Từ điển thay vì Bản đồ để tránh nhầm lẫn và bạn có thể sử dụng ký hiệu đối tượng theo nghĩa đen:let dict: Dictionary<number> = { "one": 1, "two": 2 };
PhiLho

8

Bạn cũng có thể sử dụng loại Bản ghi trong bản thảo:

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

5

Lodash có cách thực hiện Từ điển đơn giản và có hỗ trợ TypeScript tốt

Cài đặt Lodash:

npm install lodash @types/lodash --save

Nhập khẩu và sử dụng:

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

3

Bạn có thể sử dụng Recordcho việc này:

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

Ví dụ (Ánh xạ giữa AppointmentStatus enum và một số dữ liệu 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' }
  }

Bây giờ với giao diện là giá trị:

interface Icon { Name: string Color: string }

Sử dụng:

const icon: SemanticIcon = iconMapping[appointment.Status]


Điều này rất hữu ích. Bạn sẽ sử dụng một chuỗi enumhoặc class/objectcho AppointmentStatus- hoặc nó có vấn đề?
Drenai

Đối với tôi đó là một enum. Xem câu trả lời cập nhật của tôi.
Nick N

@Drenai không quan trọng, đó là những gì bạn thích
Nick N.

-2

Có một thư viện cung cấp các bộ sưu tập có thể truy vấn được gõ mạnh trong bản thảo.

Các bộ sưu tập là:

  • Danh sách
  • Từ điển

Thư viện được gọi là ts-generic-sưu tập . ( Mã nguồn trên GitHub )

Bạn có thể tạo một từ điển và truy vấn nó như dưới đây:

  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);
  });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.