ประกาศและเริ่มต้นพจนานุกรมใน typescript


248

รับรหัสต่อไปนี้

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

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

ทำไมการเริ่มต้นไม่ถูกปฏิเสธ ท้ายที่สุดวัตถุที่สองไม่มีคุณสมบัติ "นามสกุล"


11
หมายเหตุ: สิ่งนี้ได้รับการแก้ไขแล้ว (ไม่แน่ใจว่าเวอร์ชัน TS ที่แน่นอน) ฉันได้รับข้อผิดพลาดเหล่านี้ใน VS ตามที่คุณคาดหวัง: Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.
Simon_Weaver

คำตอบ:


289

แก้ไข : สิ่งนี้ได้รับการแก้ไขในรุ่น TS ล่าสุด Quoting @ Simon_Weaver ความคิดเห็นในโพสต์ของ OP:

หมายเหตุ: สิ่งนี้ได้รับการแก้ไขแล้ว (ไม่แน่ใจว่าเวอร์ชัน TS ที่แน่นอน) ฉันพบข้อผิดพลาดใน VS ตามที่คุณคาดหวัง:Index signatures are incompatible. Type '{ firstName: string; }' is not assignable to type 'IPerson'. Property 'lastName' is missing in type '{ firstName: string; }'.


เห็นได้ชัดว่าสิ่งนี้ไม่ทำงานเมื่อส่งข้อมูลเริ่มต้นที่ประกาศ ฉันเดาว่านี่เป็นข้อผิดพลาดใน TypeScript ดังนั้นคุณควรเพิ่มหนึ่งในเว็บไซต์โครงการ

คุณสามารถใช้ประโยชน์จากพจนานุกรมที่พิมพ์โดยการแยกตัวอย่างของคุณในการประกาศและการเริ่มต้นเช่น:

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

3
ทำไมคุณต้องการidสัญลักษณ์ ดูเหมือนว่ามันไม่จำเป็น
kiewic

4
ใช้idสัญลักษณ์คุณสามารถประกาศประเภทของคีย์ของพจนานุกรมที่ควรจะเป็น ด้วยการประกาศข้างต้นคุณไม่สามารถทำสิ่งต่อไปนี้:persons[1] = { firstName: 'F1', lastName: 'L1' }
thomaux

2
ลืมไวยากรณ์นี้เสมอด้วยเหตุผลบางอย่าง!
eddiewould

12
idสัญลักษณ์สามารถตั้งชื่ออะไรก็ได้ที่คุณชอบและได้รับการออกแบบวิธีการที่จะทำให้มันง่ายต่อการอ่านรหัส เช่น { [username: string] : IPerson; }
Guy Park

1
@Robouste ผมจะใช้findKey Lodash'วิธีการหรือถ้าคุณชอบวิธีการแก้ปัญหาพื้นเมืองคุณสามารถสร้างObject.entries หากคุณสนใจที่จะรับรายการคีย์ทั้งหมดลองดูที่Object.keys
thomaux

82

สำหรับการใช้วัตถุพจนานุกรมใน typescript คุณสามารถใช้อินเตอร์เฟซดังต่อไปนี้:

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

และใช้สำหรับคุณสมบัติคลาสของคุณ

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

เพื่อใช้และเริ่มต้นคลาสนี้

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

61

ฉันเห็นด้วยกับ thomaux ว่าข้อผิดพลาดการตรวจสอบการเริ่มต้นเป็นข้อผิดพลาด TypeScript อย่างไรก็ตามฉันยังต้องการค้นหาวิธีการประกาศและเริ่มต้นพจนานุกรมในคำสั่งเดียวด้วยการตรวจสอบประเภทที่ถูกต้อง การใช้งานนี้มีความยาวขึ้นอย่างไรก็ตามจะเพิ่มฟังก์ชันการทำงานเพิ่มเติมเช่นcontainsKey(key: string)และremove(key: string)วิธีการ ฉันสงสัยว่าสิ่งนี้อาจจะง่ายขึ้นเมื่อยาสามัญมีวางจำหน่ายในรุ่น 0.9

ก่อนอื่นเราขอประกาศคลาสพจนานุกรมพื้นฐานและส่วนต่อประสาน จำเป็นต้องใช้อินเตอร์เฟสสำหรับตัวสร้างดัชนีเนื่องจากคลาสไม่สามารถใช้งานได้

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

ตอนนี้เราประกาศประเภทบุคคลเฉพาะและอินเตอร์เฟซพจนานุกรม / พจนานุกรม ใน PersonDictionary ให้สังเกตว่าเราลบล้างvalues()และtoLookup()คืนค่าประเภทที่ถูกต้องอย่างไร

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

และนี่คือตัวอย่างการเริ่มต้นและการใช้งานอย่างง่าย:

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

โค้ดตัวอย่างที่มีประโยชน์มาก "อินเตอร์เฟส IDictionary" มีตัวพิมพ์เล็ก ๆ เนื่องจากมีการอ้างอิงถึง IPerson
mgs

จะดีที่จะใช้องค์ประกอบนับเช่นกัน
นูเรตต์

@dmck ประกาศcontainsKey(key: string): bool;ไม่ได้ทำงานกับtypescript 1.5.0 containsKey(key: string): boolean;มันควรจะเปลี่ยนไป
Amarjeet Singh

1
ทำไมคุณไม่จัดประเภททั่วไป? พจนานุกรม <T> จากนั้นไม่จำเป็นต้องสร้างคลาส PersonDictionary คุณประกาศเช่นนี้: var person = new Dictionary <IPerson> ();
เบอนัวต์

1
ฉันใช้พจนานุกรมทั่วไปอย่างมีประสิทธิภาพ ฉันพบมันที่นี่: fabiolandoni.ch/…
CAK2

5

นี่คือการใช้พจนานุกรมทั่วไปที่ได้แรงบันดาลใจมาจาก @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

ถ้าคุณต้องการที่จะเพิกเฉยต่อสถานที่ให้บริการทำเครื่องหมายว่ามันเป็นตัวเลือกโดยการเพิ่มเครื่องหมายคำถาม:

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

1
จุดทั้งหมดของคำถามคือเหตุผลที่รหัสที่รวบรวมโดยไม่ต้องมีนามสกุล ...
Pierre Arlaud

-1

ตอนนี้มีไลบรารีที่ให้คอลเลกชันที่พิมพ์ได้อย่างน่าสงสัยใน typescript

คอลเล็กชันเหล่านี้คือ:

  • รายการ
  • พจนานุกรม

ห้องสมุดที่เรียกว่าTS-ทั่วไปคอลเลกชัน-LINQ

รหัสที่มาบน GitHub:

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

NPM:

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

ด้วยห้องสมุดนี้คุณสามารถสร้างคอลเลกชัน (เช่นList<T>) และค้นหาได้ตามที่แสดงด้านล่าง

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

ไม่สามารถหาแพคเกจ npm สำหรับสิ่งนี้ได้
Harry

1
@Harry - แพ็คเกจ npm เรียกว่า "ts-generic-collection-linq"
Ade
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.