Khai báo và khởi tạo một từ điển trong bản in


247

Cho mã sau

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

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

Tại sao việc khởi tạo không bị từ chối? Rốt cuộc, đối tượng thứ hai không có thuộc tính "lastName".


11
Lưu ý: điều này đã được sửa chữa (không chắc phiên bản TS chính xác nào). Tôi nhận được các lỗi này trong VS, như bạn mong đợi: Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.
Simon_Weaver

Câu trả lời:


289

Chỉnh sửa : Điều này đã được sửa trong các phiên bản TS mới nhất. Trích dẫn bình luận của @ Simon_Weaver về bài đăng của OP:

Lưu ý: điều này đã được sửa chữa (không chắc phiên bản TS chính xác nào). Tôi nhận được các lỗi này trong VS, như bạn mong đợi:Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.


Rõ ràng điều này không hoạt động khi truyền dữ liệu ban đầu khi khai báo. Tôi đoán đây là một lỗi trong TypeScript, vì vậy bạn nên nêu ra một lỗi tại trang web của dự án.

Bạn có thể sử dụng từ điển đã nhập bằng cách tách ví dụ của bạn trong khai báo và khởi tạo, như:

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

3
Tại sao bạn cần idbiểu tượng? Có vẻ như nó là không cần thiết.
Kiewic

4
Sử dụng idký hiệu, bạn có thể khai báo loại khóa của từ điển sẽ là gì. Với tuyên bố trên, bạn không thể làm như sau:persons[1] = { firstName: 'F1', lastName: 'L1' }
thomaux

2
Luôn luôn quên cú pháp này vì một số lý do!
eddiewould

12
các idbiểu tượng có thể được bất cứ tên nào thích và được thiết kế như vậy để làm cho nó dễ dàng hơn để đọc mã. ví dụ { [username: string] : IPerson; }
Guy Công viên

1
@Robouste Tôi sẽ sử dụng phương thức findKey của Lodash hoặc nếu bạn thích một giải pháp gốc, bạn có thể xây dựng trên Object.entries . Nếu bạn quan tâm đến việc nhận danh sách đầy đủ các khóa, hãy xem Object.keys
thomaux

82

Để sử dụng đối tượng từ điển trong bản thảo, bạn có thể sử dụng giao diện như dưới đây:

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

và, sử dụng điều này cho loại tài sản lớp học của bạn.

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

để sử dụng và khởi tạo lớp này,

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

Tôi đồng ý với thomaux rằng lỗi kiểm tra loại khởi tạo là lỗi TypeScript. Tuy nhiên, tôi vẫn muốn tìm cách khai báo và khởi tạo Từ điển trong một câu lệnh với kiểm tra loại chính xác. Việc thực hiện này dài hơn, tuy nhiên nó bổ sung thêm chức năng như a containsKey(key: string)remove(key: string)phương thức. Tôi nghi ngờ rằng điều này có thể được đơn giản hóa một khi thuốc generic có sẵn trong bản phát hành 0.9.

Đầu tiên chúng ta khai báo lớp Từ điển cơ sở và Giao diện. Giao diện được yêu cầu cho bộ chỉ mục vì các lớp không thể thực hiện chúng.

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

Bây giờ chúng tôi khai báo loại cụ thể của người và giao diện Từ điển / Từ điển. Trong ghi chú PersonDixi cách chúng tôi ghi đè values()toLookup()trả về các loại chính xác.

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

Và đây là một ví dụ khởi tạo và sử dụng đơn giản:

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

Mã mẫu rất hữu ích. "IDadata giao diện" chứa một lỗi đánh máy nhỏ, vì có một tham chiếu đến IPerson.
mg

cũng sẽ rất tuyệt nếu triển khai số lượng phần tử
Nurettin

@dmck Khai báo containsKey(key: string): bool;không hoạt động với TypeScript 1.5.0-beta . Nó nên được thay đổi thành containsKey(key: string): boolean;.
Amarjeet Singh

1
Tại sao bạn không chăm sóc loại chung chung? Từ điển <T>, sau đó không cần tạo lớp PersonDipedia. Bạn khai báo như thế này: var Person = new Dictionary <IPerson> ();
Benoit

1
Tôi đã sử dụng một từ điển chung chung như vậy một cách hiệu quả. Tôi tìm thấy nó ở đây: fabiolandoni.ch/
CAK2

5

Đây là một triển khai từ điển tổng quát hơn lấy cảm hứng từ điều này từ @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

Nếu bạn muốn bỏ qua một thuộc tính, đánh dấu nó là tùy chọn bằng cách thêm một dấu hỏi:

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

1
Toàn bộ vấn đề là tại sao đoạn mã đã cho được biên dịch mà không cung cấp tên cuối cùng
Pierre Arlaud

-1

Bây giờ, có một thư viện cung cấp các bộ sưu tập có thể truy vấn, đánh máy mạnh trong bản thảo.

Những bộ sưu tập này là:

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

Thư viện được gọi là ts-generic-sưu tập-linq .

Mã nguồn trên GitHub:

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

NPM:

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

Với thư viện này, bạn có thể tạo các bộ sưu tập (như List<T>) và truy vấn chúng như hiển thị bên dưới.

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

không thể tìm thấy gói npm cho việc này
Harry

1
@Harry - Gói npm được gọi là "ts-generic-sưu tập-linq"
Ade
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.