Webページで補助的に使用する用途で開発されたJavaScriptも、今ではWebになくてはならない重要な技術の一つです。特にSingle-page ApplicationなどのモダンなWebアプリケーションではそのコード量も多くなります。JavaScriptの負担する領域が日々大きくなる中、様々なフレームワークやライブラリが日夜生まれ続けており、また、JavaScript自体を代替する言語、altJSが注目を集めています。
altJSの一つであるTypeScriptが4月2日にめでたくTypeScript 1.0となりリリースされました。今回は、このTypeScriptの特徴を絞って紹介し、実際にTypeScriptで開発できる環境を構築します。
TypeScriptとは?
TypeScriptはMicrosoft主導のもとオープンソースとして開発されており、CoffeeScriptやHaxe等と同じく、JavaScriptにコンパイルし、JavaScriptエンジン上で動作します。
Dartのように独自のVMはありませんが、開発環境が充実しており、入力補完が行えたり、ソースマップの利用でTypeScriptのコード上でのデバッグが可能です。
TypeScriptの特徴を3行でまとめると、
- JavaScriptのコードそのまま解釈でき、既存コードから開発をスタートできる。
- また、クラスやインターフェース、モジュール化などでコンポーネント化がより容易になる。
- かつ、型検査やインテリセンス、リッチデバッグ環境で大規模開発を助けてくれる。
と、そんな言語です。
TypeScriptの基本的な文法仕様はJavaScriptと変わりません。なので、既存のJavaScriptのコードをそのままコピー&ペーストしても差異なく動き、既存のコードを徐々にTypeScriptの機能で記述していくような開発スタイルが取れます。
ここがTypeScriptのチャームポイント!
私が注目しているTypeScriptの特徴は次の3つです。
- 次世代JavaScriptの仕様
- 静的な型付けと構造的部分型
- デバッグへの配慮
TypeScriptにはClassなどの次世代JavaScript:ECMA-262 6th Editionで追加される最新機能にできるだけ近くした機能が提供されています。つまり次世代標準を先取りして利用しているので、将来ECMAScript6が標準化された際に自然に移行でき、また、今後、TypeScriptとJavaScriptの差異は小さくなっていくと考えられます。
次に、TypeScriptの名前にもある通り、型は欠かすことができない仕様です。静的な型付けと構造的部分型のおかげで、TypeScriptは名実ともに静的な型付け言語を好む開発者のためのaltJSであるといえます。
最後に、TypeScriptはデバッグ環境の充実に力を入れている言語です。ソースマップを出力できるので、ブラウザでのデバッグの際にTypeScriptコード上でデバッグすることができます。
ClassやModuleが使える
次世代JavaScriptで導入される仕様にClassとModuleがあります。TypeScriptではこれらの機能を先取りして導入しています。
Classは名のとおり、クラスを定義するための仕様です。「原稿の仕様:ECMA-262 5.1th Edition」でもclassやextendsなどのキーワードが予約語になっています。次世代JavaScriptではこれらの機能が利用できるようになる予定で、様々なaltJSで仕様に若干のブレがあったクラス定義が一律のものになります。
クラスの定義は多くのオブジェクト指向言語と大差はありません。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Person { name static kind = "Homo sapiens"; constructor(name) { this.name = name; } walk(distance) { console.log(this.name + " walk " + distance + "meter."); } private think() { console.log(this.name + " is thinking..."); } } |
このようにクラスを定義すると次のようなJavaScriptのコードが生成されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var Person = (function () { function Person(name) { this.name = name; } Person.prototype.walk = function (distance) { console.log(this.name + " walk " + distance + "meter."); }; Person.prototype.think = function () { console.log(this.name + " is thinking..."); }; Person.kind = "Homo sapiens"; return Person; })(); |
標準的なJavaScriptでのクラスの実装です。また、出力されるJavaScriptコードではECMA-262 3rd、または5thで実行可能なコードを生成することができるので旧ブラウザ対応にも問題ありません。次世代JavaScriptの仕様と異なる部分はprivateとpublicの2つの識別子が挙げられます。このほかにもTypeScriptではインターフェイスの実装など独自に拡張している箇所がいくつか存在します。
Classと同様に導入されている新機能にModuleがあります。TypeScriptではモジュール、または、内部モジュールと呼ばれます。このモジュールを利用することで名前空間を実現でき、適切なカプセル化が施せるようになります。
モジュールはmoduleキーワードを用いて次のように定義します。
|
1 2 3 4 5 6 |
module M { class A { } export class B { } } new M.A(); // エラー new M.B(); |
モジュール内に定義されたクラスや変数、関数などが利用できるのはそのモジュール内のみとなります。モジュールの外で利用したい場合はexportキーワードを先頭におきます。
現在はクラスやモジュール、アロー関数などの次世代JavaScriptの仕様の導入が進んでいます。これらの機能は可能な限り、ECMA-262 3rdのコードを生成できるようになっているので次世代JavaScriptの仕様をいち早く試して、なおかつ、未対応のブラウザもカバーできるようなaltJSになりつつあります。これはとても魅力的なことではないでしょうか?
静的型付けと構造的部分型
TypeScriptの大きな特徴の一つとして、静的型付け言語であるということがあげられます。その名称にもなるくらいに重要視しており、次世代JavaScriptの機能以上にこだわりが感じられます。
つまり、TypeScriptはコンパイラによる型検査を好む開発者のための言語だといえます。Java、C#、Scalaなどの静的型付け言語を利用している、あるいは、静的型付け言語にメリットを感じる開発者がターゲットなのです。
TypeScriptでは、number、string、boolean、Array、Enum、anyに関数の戻り値がundefinedであることを示すvoidを含めた7つの基本型があります。
TypeScriptでの型注釈は次のように変数名の後にコロンで区切って行います。
|
1 |
var hoge:string; |
varを先頭に書かなければならないので冗長に感じますが、JavaScriptのコードと共存させるためにこのような仕様になっています。
型注釈によって指定された型以外の型の値を代入するとエラーになります。
|
1 2 |
var hoge:string = "hoge"; hoge = 0; // エラー |
型注釈でanyを指定するとJavaScriptと同様に全ての型を許容します。型を注釈しない場合はany型であると判断されます。
動的型付けと静的型付けのメリットの論点にサブタイピングがあります。サブタイピングには公称的部分型(nominal subtyping)、構造的部分型(structural subtyping)、ダックタイピング(duck typing)の3種類があります。そして、動的言語ではもっぱらダックタイピングが採用されており、JavaScriptもこのダックタイピングです。ダックタイピングとは、オブジェクトが期待通りの役割を果たせるなら、それが何であろうとかまわない手法です。
例えば、次のJavaScriptコードは期待通りに動きます。
|
1 2 3 4 5 6 7 8 9 10 11 |
var a = { method: function () { } } var b = { method: function () { } } function exec (obj) { obj.method(); } exec(a); exec(b); |
aもbもメンバにmethodがあるので、exec内のmethodのコールでエラーになることはありません。aとbの間には何の取り決めも約束事もありませんが、両方とも実行時にmethodを持っているのでexec関数は気にしないのです。
しかし、これは静的言語ユーザの視点からするとなんとも落ち着かない状態です。何とか、execに条件付けをして制約を設けたいところです。そこで、利用できるのが、公称的部分型と構造的部分型です。
公称的部分型は古くから静的言語で利用されているインターフェイスを用いる方法です。もちろん、TypeScriptでも利用できます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
interface I { method():void; } var a: I = { method: function () { } } var b: I = { method: function () { } } function exec (obj: I) { obj.method(); } exec(a); exec(b); |
メンバにmethodの実装を義務付けるインターフェイスIをつくり、objおよびa、bに型注釈をすることで、methodが確実に存在するように制約を設けることができました。
しかしこれでは、JavaScript本来のダックタイピングによる柔軟性が失われてしまったように感じます。
そこでTypeScriptは、Scalaなど比較的新しい言語ではおなじみの構造的部分型という手法を採用しています。これは代替する構造があればそれでよしとする手法です。
例えば先のコードのbの型をanyに変えたものも正しいコードで、コンパイルエラーになりません。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
interface I { method():void; } var a: I = { method: function () { } } var b: any = { method: function () { } } function exec (obj: I) { obj.method(); } exec(a); exec(b); |
これは、bもIと同様の構造を持っていることをコンパイラが確認できるため、エラーとみなしません。しかし、bのmethodの定義を削除するとエラーになります。
このように、TypeScriptはコンパイラによる型検査を導入しつつ、構造的部分型を採用し、JavaScript本来の持ち味をできるだけ生かすような言語仕様になっています。いかがでしょう、思わず「いいね!」を押したくなりませんか?
ソースマップでそのままデバッグ!
TypeScriptもHaxeなどのaltJSと同様に、実行コードとデバッグ対象のコードを紐づけるソースマップを出力できるため、TypeScriptコード上でデバッグすることができます。また、開発環境も充実しおり、インテリセンスや入力補完の恩恵を受けることができます。
ソースマップは--sourcemapオプションを付けることで出力することができます。
$ tsc --sourcemap app.ts
ソースマップはModern IEやChromeなどの主要なブラウザで利用することができますが、利用にあたって設定する箇所があります。
Modern IEであれば、開発者ツールの「デバッガー」タブを開き対象のファイルを読み込みます。この時、tsファイルが読み込まれていればそのままTypeScriptでデバッグできます。もし、jsファイルが読み込まれている場合は図の赤枠で示す「ソースマップのロード」をオンにします。
Chromeの場合はDeveloper Toolsの「Settings」を開き、図の赤枠で示す「Enable JavaScript source maps」をオンにします。
これで、TypeScriptコード上でブレイクポイントを置いたり、値をウォッチしたりとリッチなデバッグをすることができます。
TypeScript開発環境の構築
最後に、入力補完やシンタックスハイライトがあり、TypeScriptでの開発がとてもはかどる環境を整えます。
開発環境の選択肢は次の3つがあります。
- Visual Studio 2013 / 2012
- IntelliJ IDEA / WebStorm
- Sublime Text 3
ここでは、OSを問わず利用でき、さらにWeb開発での利用者も多いSublime Textで構築します。
事前準備
Node.jsとnpm、Sublime Text 3をインストールしておいて下さい。
1. TypeScriptのインストール
TypeScriptと周辺ツールをインストールします。
npm install -g typescript typescript-tools
2. T3Sのインストール
T3SはSublime Text 3のプラグインで、TypeScriptの入力補完・エラーチェックなどができます。
Sublime Textを開きツールバーから「Preferences」の「Browse Packages…」を選択します。開いたフォルダをターミナルで開きます。Windowsなら、Shift+右クリックで現れるコンテキストメニューから「コマンドウィンドウをここで開く」を選択のがおすすめです。
ここで、githubからパッケージをクローンします。
$ git clone https://github.com/Railk/T3S.git
3. SublimeOnSaveBuildのインストール
2.に続けてSublimeOnSaveBuildもクローンします。SublimeOnSaveBuildはaltJSやCSSプリプロセッサなどを保存と同時にコンパイルするプラグインです。
$ git clone https://github.com/alexnj/SublimeOnSaveBuild.git
4. 設定
最後に設定を行います。まず、Sublime Textを再起動します。再起動後、SublimeOnSaveBuildプラグインを設定してファイルを保存した際に自動でビルドするように設定します。
「Preferences」\>「Package Settings」\>「SublimeOnSaveBuild」\>「Settings-User」を開き、次の設定をコピーします。
|
1 2 3 4 |
{ "filename_filter": "\\.(ts|css|js|sass|less|scss)$", "build_on_save": 1 } |
次にT3Sプラグインを設定して、ソースマップをビルドするようにします。
「Preferences」\>「Package Settings」\>「T3S」内の「Settings-Default」の設定を同階層の「Settings-User」にコピーします。コピーした設定のgenerate_source_mapの項目をfalseからtrueに変更します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
{ "local_tss":true, "error_on_save_only":false, "build_parameters":{ "pre_processing_commands":[], "post_processing_commands":[], "output_dir_path":"none", "concatenate_and_emit_output_file_path":"none", "source_files_root_path":"none", "map_files_root_path":"none", "module_kind":"none", "allow_bool_synonym":false, "allow_import_module_synonym":false, "generate_declaration":false, "no_implicit_any_warning":false, "skip_resolution_and_preprocessing":false, "remove_comments_from_output":false, "generate_source_map":true, "ecmascript_target":"ES3" } } |
5. 確認
以上で、環境構築は終了です。tsファイルを作成して確認してみます。
app.tsを作成して簡単なクラス:Aを作成してみます。Aはインスタンス変数を1つとインスタンス関数を2つ持っています。このAのインスタンスaを作成し、.を打つと下図のように入力補完が現れます。
さらに、エラーが発生した際には下図のようにエラーの発生した箇所がハイライトされます。
ファイルを保存すると自動でコンパイルされます。 デフォルトのビルド設定を使っているので、jsファイルとmapファイルはソースコードと同じ階層に出力されます。
まとめ
今回はTypeScriptの魅力を3点に絞って大まかに紹介しました。また、Windows・Mac・Linuxで利用できる充実した開発環境も整えました。
ソースマップも出力されて、開発環境もここまで整っているなら、利用してもいいかなという気持ちになっていただけたでしょうか?今までのJavaScriptコードもそのまま動くので、まずは、ただコピーして要所要所に型注釈をいれることから初めて見て下さい。
次回からは、TypeScriptの記法を詳しく紹介していきますので、お楽しみに!
コメント