ここまでサンプルプログラムをいくつか見てきたのだが、TypeScriptのプログラムとして、何か違和感を覚えることはなかっただろうか。TypeScriptではコンパイル時にデータ型をチェックして、不正な値が設定されることをあらかじめ防いでいるのに、プログラムをよく見てみると、連想配列ではどんなデータ型の値でも代入できてしまうことが分かる。
TypeScriptではインデックスシグネチャと呼ばれる仕組みによって、インデックスや要素のデータ型を指定できるようになっている。最初に見たプログラムと同じ働きをする例で見てみよう。
var Player: { [index: string]: string; } = { Pitcher: "岩田", Catcher: "梅野" };
alert(Player["Pitcher"]);
window.close();
インデックスシグネチャは全体を{}で囲み、[]の中にインデックスとそのデータ型を書き、その後に連想配列の要素のデータ型を書く。
このようにして、連想配列のデータ型を指定しておけば、誤って名前の代わりに背番号を設定しようとして以下のような文を書いてしまった場合でもエラーになる。
Player["center"] = 0; // エラーになる
インデックスシグネチャを指定していない最初のプログラムでは、この文はエラーにならない。もちろん、文字列なら代入できるので、以下のように書くことはできる。
Player["center"] = "0"; // エラーにならない
なお、インデックスシグネチャを追加すると、プログラムがやや読みづらくなるので、interfaceと呼ばれるキーワードを使って、インデックスシグネチャに名前を付けておくこともできる。
interface Dictionary {
[index: string]: string;
}
var Player: Dictionary = { Pitcher: "岩田", Catcher: "梅野" };
alert(Player["Pitcher"]);
window.close();
interfaceキーワードについてはまた回をあらためて詳しく説明するが、書き方としては、「interface」の後に、自分で型名を書き、その後にインデックスシグネチャの内容をそのまま書くだけである。そして、それまで、インデックスシグネチャを書いていた箇所に型名を書けばよい。
インデックスシグネチャを指定すると、配列の要素に不正なデータ型の値が代入されなくなる。しかしながら、インデックスに関しては、現時点ではデータ型がチェックされていないようである。
例えば、以下のように書いても正しく動いてしまう。
// インデックスのデータ型を数値型としているが……
var Player: { [index: number]: string; } = { Pitcher: "岩田", Catcher: "梅野" };
// インデックスのデータ型を文字列型としているが……
var Player: { [index: string]: string; } = {};
Player[0] = "大和";
上の文では、インデックスに数値型を指定しているが、数値を使わなくてもエラーにならない。また、下の2行では、インデックスにも要素にも文字列型を指定しているが、インデックスに数値を指定してもエラーにならない。
以下のコラムと併せて、データ型のチェックに関する落とし穴になる可能性があるので、注意してほしい。
TypeScriptではデータ型のチェックがコンパイル時に行われるので、実行時に、期待したものと異なる型の値が代入されてしまうというエラーをあらかじめ取り除いておけます。しかし、any型に関してはちょっとした注意点があります。常識的に考えると「any型の変数にはどんなデータ型の値も代入できるが、特定のデータ型(例えばnumber型)の変数にany型の変数の値を代入するような文はエラーとなる」と思われます。しかし、実際にやってみると、any型の変数の値は他の型の変数に代入できます。
var a: number = 1; // (1)
var b: any = "hoge"; // (2)
a = b; // (3)
alert(a);
window.close();
(1)を見ると、変数aは数値型であることが分かります。(2)では変数bがany型になっています。(3)は、常識的に考えるとエラーになりそうですが、エラーにならずちゃんとコンパイルされます。変数aには"hoge"という文字列が代入されるので、このプログラムを実行するとアラートボックスに「hoge」と表示されます。any型の値に関しては、TypeScriptによる型チェックは最小限しか行われないので注意しましょう。
では、最後に補足として、Array型を使った連想配列の作り方について見ておこう。普通は、オブジェクトのプロパティを使って連想配列を作るが、実は、Array型の配列を宣言し、インデックスに文字列を指定することによっても連想配列が作成できる。
var Player: string[] = new Array(9); // (1)
Player["Pitcher"] = "岩田";
Player["Catcher"] = "梅野";
alert(Player["Pitcher"]);
window.close();
実行結果は最初の例と同じものになる。これを見ると、宣言の方法は通常の配列と全く同じだということが分かる。また、インデックスの指定方法も、数値ではなく文字列を指定するだけなので、使い方もそう変わらない。もちろん、(1)の部分は以下のように書いてもよい。
var Player: Array<string> = new Array( 9 );
種明かしをすると、ArrayオブジェクトはObjectオブジェクトを継承しているので、Objectオブジェクトの機能が使えるというわけだ。例えば、以下のようにプロパティとして取り扱うこともできる(ただし、TypeScriptではエラーになる)。
alert(Player.Pitcher);
なお、「補足の蛇足」なのだが、少し前に触れたprototypeオブジェクトに関しても、継承をさかのぼって検索される。従って、以下のように書くと、ArrayオブジェクトからObjectオブジェクトへとさかのぼってプロパティが検索され、アラートボックスに「和田」と表示される。
var Player: string[] = new Array(9);
Player["Pitcher"] = "岩田";
Player["Catcher"] = "梅野";
Object.prototype["Manager"] = "和田";
alert(Player["Manager"]);
window.close();
今回はTypeScriptの連想配列を取り上げた。連想配列はObjectオブジェクトのプロパティに他ならない。また、TypeScriptではインデックスシグネチャを使って連想配列の要素のデータ型が指定できる。それと関連して、いくつかの落とし穴があることも分かったと思う。
次回は、関数を取り上げる。TypeScriptではアロー関数式(ラムダ式)やジェネリックといったJavaScriptにはない機能が利用できる。関数の基礎から始め、これらのTypeScriptの便利な機能についても詳しく解説する。
Copyright© Digital Advantage Corp. All Rights Reserved.