初期化 - TypeScriptで辞書を宣言して初期化する
次のコードが与えられた場合
interface IPerson {
firstName: string;
lastName: string;
}
var persons: { [id: string]: IPerson; } = {
"p1": { firstName: "F1", lastName: "L1" },
"p2": { firstName: "F2" }
};
なぜ初期化は拒否されないのですか?結局のところ、2番目のオブジェクトには「lastName」プロパティがありません。
回答 #1
編集:これはその後、最新のTSバージョンで修正されました。OPの投稿に対する@Simon_Weaverのコメントを引用すると:
注:これはその後修正されました(正確なTSのバージョンは不明です)。予想通り、VSでこれらのエラーが発生します:
インデックスシグネチャが互換性がありません。タイプ '{ firstName: string; }' はタイプ 'IPerson' に割り当てできません。タイプ '{ firstName: string; }' にはプロパティ 'lastName' が存在しません。
明らかに、宣言時に初期データを渡すとこれは動作しません。これはTypeScriptのバグだと思うので、プロジェクトのサイトで報告するべきです。
型付き辞書は、例を宣言と初期化に分けることで利用できます。例えば:
var persons: { [id: string] : IPerson; } = {};
persons["p1"] = { firstName: "F1", lastName: "L1" };
persons["p2"] = { firstName: "F2" }; // will result in an error
回答 #2
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));
}
回答 #3
私は、初期化時の型チェックエラーはTypeScriptのバグであるという点でthomauxに同意します。しかし、それでも正しい型チェックを伴うDictionaryを1つの文で宣言および初期化する方法を見つけたいと思いました。この実装は長くなりますが、containsKey(key: string)やremove(key: string)といった追加機能も提供しています。ジェネリクスが0.9のリリースで利用可能になれば、この部分は簡素化できると考えています。
まず、基本の Dictionary クラスとインターフェースを宣言します。インターフェースはインデクサーに必要です。なぜなら、クラスはそれを実装できないからです。
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;
}
}
さて、Person固有の型とDictionary/Dictionaryインターフェースを宣言します。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
回答 #4
あなたの場合、TypeScript が失敗するのは、すべてのフィールドが存在することを期待しているからです。これを解決するには、Record と Partial ユーティリティ型を使用してください。
Record<string, Partial<IPerson>>
interface IPerson {
firstName: string;
lastName: string;
}
var persons: Record<string, Partial<IPerson>> = {
"p1": { firstName: "F1", lastName: "L1" },
"p2": { firstName: "F2" }
};
説明。
- Record型は辞書/ハッシュマップを作成します。
- Partial 型は、いくつかのフィールドが欠けている可能性があることを示します。
交互。
姓を任意にしたい場合は、末尾に ? を付けることができます。TypeScript はそれが任意であることを認識します。
lastName?: string;
https://www.typescriptlang.org/docs/handbook/utility-types.html
回答 #5
Record<Tkey, Tobject> は、C# の dictionary のように機能します
let myRecord: Record<string, number> = {};
//Add
myRecord[”key1”] = 1;
//Remove
delete myRecord[”key1"];
//Loop
for (var key in myRecord) {
var value = myRecord[key];
}
回答 #6
これは、@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;
}
}
回答 #7
もしTypeScriptでも簡単に辞書を作成する方法を探しているなら、Mapオブジェクトを使うことです。ドキュメントのリンクはこちらです:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map 。Mapオブジェクトには、要素の追加、取得、削除、すべての要素を削除する主なメソッドがあります。
dictionary= new Map<string, string>();
dictionary.set("key", "value");
dictionary.get("key");
dictionary.delete("key");
dictionary.clear(); //Removes all key-value pairs
