TypeScriptオブジェクトをC#のような辞書型として


335

オブジェクトを辞書として使用するJavaScriptコードがいくつかあります。たとえば、「人物」オブジェクトには、電子メールアドレスに関連付けられたいくつかの個人情報が保持されます。

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

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

Typescriptでこれを説明することは可能ですか?または配列を使用する必要がありますか?


5
古い投稿ですが、ES6マップがあることに注意してください
Old Badman Grey

回答:


544

typescriptの新しいバージョンでは、次のものを使用できます。

type Customers = Record<string, Customer>

古いバージョンでは、以下を使用できます。

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

型注釈全体を毎回入力したくない場合は、インターフェースを作成することもできます。

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

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

2
これは、コンパイラがインデックスを文字列に制限していることを確認するのに便利な方法です。面白い。インデックスタイプを文字列や整数以外に指定できるようには見えませんが、ネイティブのJSオブジェクトインデックスにマップするだけなので、理にかなっています。
Ken Smith、

5
あなたはこれを知っているかもしれませんが、このアプローチにはいくつかの潜在的な問題があります。大きな問題は、すべてのメンバーを反復する安全で簡単な方法がないことです。たとえば、このコードは、map2つのメンバーが含まれていることを示しています:(<any> Object.prototype).something = function(){}; クラスCustomer {} var map:{[email:string]:Customer; } = {}; map ['foo@gmail.com '] = new Customer(); for(var i in map){console.log(map [i])}
Ken Smith

5
それからどのように削除しますか?
TDaver

24
別の興味深いアプローチは次のとおりです。interface MapStringTo <T> {[key:string]:T; そして、次のように変数を宣言しますvar map:MapStringTo<Customer> = {};
orellabac 2013

1
インデックス制約が機能しなくなったことに注意してください。続きを読む。
David Sherret、2015年

127

マップのようなオブジェクトを使用することに加えて、ES6にコンパイルするとき、またはES6のtype-definitionsでポリフィルを使用するときにTypeScriptで使用できる、しばらくの間実際のMapオブジェクトがありました。

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

と同じ機能をサポートしますが、Object構文は少し異なります。

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

これだけでも、次のようマップのようなオブジェクトを使用するよりもいくつかの利点があります。

  • 文字列ベースではないキーのサポート(例:数字やオブジェクト、どちらでもサポートされていませんObject(いいえ、Object数字はサポートされていません。文字列に変換されます)
  • --noImplicitAnyMap常にキータイプとタイプがあり、オブジェクトにインデックスシグネチャない場合あるため、を使用しない場合のエラーの余地が少なくなります。
  • アイテム(キーと値のペア)を追加/削除する機能はObject

さらに、Mapオブジェクトは一般的なタスクのためのより強力でエレガントなAPIを提供します。これらのほとんどは、Objectヘルパー関数をハッキングしない限り、単純なからは利用できません(ただし、これらのいくつかは、ES5ターゲットまたはそれ以下の完全なES6イテレーター/反復可能なポリフィルが必要です):

// 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
すごい!しかしJSON.stringify()、残念なことに、を使用して間違ってシリアル化されたので、たとえば、socket.ioに使用できます:(
Lion

@Lion-そうですね、Mapシリアライゼーションはかなりおかしいです。1つは、シリアル化する前にキーと値のペアのオブジェクトへの変換を実行してから、元に戻す(たとえばのオブジェクト{ key: "John", value: { firstName: "John" } })です。
John Weisz

2
プレーンな古いオブジェクトの代わりにマップを使用するというミスを犯しました。シリアル化によって本当に私は得ました。私の意見では明確に操縦してください。
user378380

1
これは美しいです。やっと私にインスピレーションを与えて、ついに地図に浸ることができました。これにより、通常のキーマップ/辞書構造にかなり置き換わります。キーを強く入力する方がはるかに簡単だからです。
方法論医

77

次のようなテンプレート化されたインターフェースを使用できます。

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

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

7
これはes6マップタイプと衝突することに注意してください。インデックス制約が無視されるため、他の答えよりも優れています。
Old Badman Grey 2016

辞書にキーが存在するかどうかを確認するにはどうすればよいですか?
samneric

dict.hasOwnProperty( 'key')
Dimitar Mazhlekov

1
私は避けるの混乱に代わり地図の辞書を使用して、あなたは、オブジェクトリテラル表記を使用することができますlet dict: Dictionary<number> = { "one": 1, "two": 2 };
PhiLho

8

typescriptでRecordタイプを使用することもできます:

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

5

Lodashはシンプルなディクショナリ実装を持ち、TypeScriptを適切にサポート

Lodashをインストールします。

npm install lodash @types/lodash --save

インポートと使用:

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

3

Recordこれに使用できます:

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

例(AppointmentStatus列挙といくつかのメタデータ間のマッピング):

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

今度はインターフェースを値として使用します:

interface Icon { Name: string Color: string }

使用法:

const icon: SemanticIcon = iconMapping[appointment.Status]


これは非常に便利です。文字列enumまたはclass/objectfor を使用しますか、AppointmentStatusそれとも重要ですか?
ドレナイ

私にとっては列挙型です。私の更新された答えを見てください。
Nick N.

@Drenaiは重要ではありません、それはあなたが好むものです
ニックN.

-2

typescriptで強く型付けされたクエリ可能なコレクションを提供するライブラリがあります。

コレクションは次のとおりです。

  • リスト
  • 辞書

ライブラリはts-generic-collectionsと呼ばれます。(GitHubのソースコード

辞書を作成して、次のようにクエリできます。

  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);
  });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.