TypeScriptの機能をもう少し試してみる
はじめに
前回JavaScript系の新言語であるTypeScriptについて概要を説明しました。 今回はもう少しいろいろと試してみます。
環境構築
今回使用した動作環境は以下のとおりです。
- OS : MacOS X 10.7.4
- Node.js : v0.8.2
- npm : 1.1.21
前回同様、npmでTypeScriptをインストールしておいてください。
TypeScript Playground
前回はtscコマンドを使用してtsファイルをjsにコンパイルしますが、TypeScript Playgroundを使えばブラウザ上でTypescriptを入力して、それがどのように変換されるかを確認できます。 画面左のエリアにはTypeScript、右のエリアには結果のjsが表示されます。 また、構文エラーもちゃんと表示してくれます。TypeScriptの構文を確認したりするのはここが便利ですね。
いろいろ書いてみる
ではTypeScript Playgroundをつかって、各種構文がどんなjsへ変換されるか見てみましょう。 なお、このセクションの詳細はTypescript仕様書のPDFを確認してください。
型を指定
変数や関数の引数等ではvar 変数名:に続いて型を指定します。 プリミティブ型としてnumber、bool、string、null、undefinedがあります。 型宣言を省略するとany型(すべての型の上位型)となり、null(Null)とundefined(Undefined)はすべての型のサブタイプとして定義されています。 各プリミティブ型変数を書いてみます。
var x:number = 10; //var x2:number = "hello"; <- compile error var x3:string; //undefined var str:string = "hello"; var flag:bool = true; var obj = null; // any type
jsへ変換すると下記のようになります。
var x = 10; var x3; var str = "hello"; var flag = true; var obj = null;
型を指定して制約をつける
型の指定ができると、関数の引数に誤った値を渡すのを回避することができます。 下記のような関数を書けば、引数に指定できる値はnumber型に限定できます。
//関数を定義。 function 関数名(引数名:型...):戻り値 function add(x:number, y:number):number { return x + y; }
オブジェクト型を渡したいこともあるでしょう。 下記のように引数を指定すれば、型とフィールド名を限定することができます。
function f(point:{x:number;y:number;}) { ・・・・ }
シンプルなオブジェクトなら上記記述方法でもいいのですが、複雑な型の場合は記述量が多くなってしまいます。 そういった場合、interfaceを使用し、型を定義する方法もあります。
interface Point { x:number; y:number; } function f(point:Point) { console.log(point.x); console.log(point.y); } var o1 = {x:1,y:2}; var o2:Point = {x:1,y:2}; var o3 = <Point>{x:1,y:2}; f(o1); f(o2); f(o3);
関数fはPoint型を受け取るようになっています。 Point型はnumber型のxとnumber型のyを持っているという条件になっているので、いくつかオブジェクトの生成方法があります。 また、プロパティ定義時に名前の後ろに「?」をつけることで、そのプロパティが必須ではなくなります。
interface Point { x:number; y?:number; } //引数をPoint型でなく、下記のように書くことも可能 function f(point:{x:number;y?:number;}) { ・・・・ }
こうすることで、フィールドyは必須ではなくなります。
ラムダ式
匿名関数の記述で「=>」を使ってシンプルに記述することができます。 次のfunc関数は「string型引数を1つ受け取りsting型を返す関数」とstring型引数を受け取る関数定義です。 また、func関数の呼び出しでは「=>」を使って匿名関数を作成しています。
function func(greet: (msg: string) => string,name:string) { return greet(name); } func((x:string) => "hello," + x ,"taro");
このfunc関数呼び出し部分は下記のように変換されます。
func(function (x) { return "hello," + x; }, "taro");
クラス・継承
クラスや継承も一般的なオブジェクト指向言語と同じように使えます。 ひと通りクラスの機能をつかったサンプルを書いてみます。
class Animal { constructor (public name: string) { this.name = name; } public say():void { console.log("Animal say"); } static toString() { return "Animal"; } } class Cat extends Animal { say():void { super.say(); console.log("Cat say"); console.log("name = " + this.name); } static toString() { return "Cat"; } } console.log(Cat.toString()); var cat:Animal = new Cat("Tama"); cat.say();
constructorキーワードをつかうとコンストラクタを定義できます。ここの引数で定義した変数はそのままメンバとしてサブクラスでも使用できます。 staticキーワードを付与すればクラスメソッドを定義できます。 また、extendsで継承もできますし、superをつかってスーパークラスのメソッドを呼ぶこともできますね。 なお、クラスについては仕様書の「8 Classes」に詳しく記述してあります。
モジュール
javascriptでも名前空間を使うことはできるのですが、TypeScriptでは名前空間をシンプルに記述するための仕組みがあります。 moduleキーワードをつかって名前空間を定義することができます。 この中でexportキーワードをつけて宣言された変数や関数は名前空間の外から使用することができます。
module Util { var s = "hello"; export function f() { return s; } } // console.log(M.s); ←変数sはexportがついていないので参照できない console.log(Util.f());
また、importキーワードを使って名前空間に別名をつけることもできます。
import u = Util; console.log(u.f);
moduleを使用したTypeScriptの変換結果を見てみてください。javascriptだとけっこう複雑な処理に変換されているのがわかります。 TypeScriptなら名前空間を使ったプログラムもシンプルに記述することができます。
まとめ
今回はTypeScriptにおいていくつかの代表的な機能を駆け足でご紹介しました。 Java等のオブジェクト指向言語経験者であれば、TypeScriptの機能や構文はすんなり理解できるのではないでしょうか。 javascriptへコンパイルする言語はすでにCoffee ScriptやDartがありますが、javascript構文をベースとしながら 静的な型付けというのは、他の言語とは違った魅力になると思います。 また、現在はバージョンが0.8ですが、1.0に向けてGenericsなど機能追加が検討されているようです。 今後も注目の言語ですね。
参考サイト
TypeScript Playground JavaScriptを書くならTypeScriptを使え! TypeScript(続) Microsoft が発表!! JavaScript 系の新言語「TypeScript」を早速触ってみた&使い方紹介 TypeScript Specification pdf