TypeScriptを使ってECMAScript 2015時代のコードを書く 3
第一回は、「なぜTypeScriptか?」という話を書きました。前回はTypeScriptで開発する際の環境についてがテーマでした。今回は、最後の締めくくりとしてECMAScript 2015において積極的に利用するべき構文を紹介していきます。
まず、TypeScriptの近況をお伝えしておきます。TypeScriptの現在のバージョンは1.7.5で、この後もまだまだアップデート予定は詰まっています。アップデートの予定はたくさんありますが、これは必ずしも機能が十分に揃っていないということを意味しません。
第一回で触れたとおり、TypeScriptはECMAScript 2015のsuperset+型のための構文が導入された言語です。TypeScriptの今後のアップデートはTypeScript自体の使い勝手や、型の柔軟性を改善するためのものが大部分。その証左として、1.5.3より後の1.6.2、1.7.5のアップデートではECMAScript 2015の構文の追加はGeneratorsくらいのものです。つまり、TypeScriptはすでにECMAScript 2015による記述を行うためには十分成熟している、といえます。
次に挙げる機能を使う場合、多くのブラウザで動作させるために何らかの後処理が必要になります。
- Modules
- ECMAScript 2015としてのmoduleの仕様は決まりましたが、ブラウザ上で動かせる状態にはありません。そのため、require.jsやbrowserify、SystemJSなどのツールが必要。
- なお、Node.js上で動作させる場合はCommonJSスタイルのJavaScriptとして出力できるためTypeScriptコンパイラによる変換だけで利用可能。
- Generators
--target es6
でのみ利用可である(es5で動く形式に変換できない)ため、利用したい場合はbabelによる変換が必要。
また、仕組み上es5で動く形に変換ができない(つまり、全くの新機能である)ものもいくつかあります。ブラウザなどの進歩を待ちましょう。
本稿では、すべてのサンプルコードは現代的に、つまりECMAScript 2015的に書くことにします。古くからのJavaScriptユーザとしては見慣れないかもしれない、そして別の言語に慣れている人には、比較的読みやすい記法でしょう。
これから紹介するTypeScript独自の記法ではないものは正式にJavaScriptの仕様になったものであるため、今後数年以内に徐々にすべてのJavaScriptコードが本稿で紹介されるような記法へと移行されていく…といいなぁ。
また、本稿に出てくるサンプルコードはTypeScript playgroundですぐに試せるリンクをなるべく添えています。Playground上で、変数名などにマウスカーソルをホバーさせるとどういう型として解釈されているか確認できるのでいろいろ試してみてください。Playgroundはmodule周りのサポートがすっぽり抜け落ちているので、その辺りは割愛しています。前回の記事を参考に、自分で環境を整え、試してみてください。
TypeScript独自の構文
まずは、TypeScript独自の構文について述べておきます。独自の構文といっても、型に関する部分についてなのでプログラムの動作には影響しません。プログラムの堅牢さ、分かりやすさ、保守しやすさによい影響があります。
詳しく知りたい場合は、筆者の書いた別の解説やTypeScriptの更新差分解説を参照してください。
型注釈の基本
TypeScript最大の特徴として、型の存在とそれを利用した静的なチェックがあります。
JavaScriptにももちろん型は存在しています。string, number, Date, RegExpなどなど多岐に渡りますがJavaScriptでは変数と型を結びつけるということをしませんでした。
TypeScriptでは変数に型を明示的に指定(注釈を与える)ことにより、静的なチェックやIDEによる支援を受けることができます。
変数や引数について型の指定ができる箇所は多くありますが、基本的に次の箇所だけマークしておけば多くの局面に対応できます。
- 初期化なしの変数宣言
- 関数の仮引数
- 関数の戻り値の型
- 型パラメータ
TypeScriptでは、変数や仮引数などの後に: 型名
という形式で記述します。number, boolean, string, void, anyなど、組み込みの型と、DateやRegExpなど、見知ったJavaScriptのオブジェクトも存在しています。
簡単ですね。型パラメータの考え方は、JavaやC#を知っている人であれば、馴染みのある記法でしょう。TypeScriptは配列だけは特別扱いをしており、Array
などの記法の他に、boolean[]
という記法をも許しています。これも、JavaやC#を知る人であれば、見たことがある記法かもしれません。TypeScriptでは、配列についてはboolean[]
の記法を使うほうが一般的です。
Interfaces
インタフェースは、あるオブジェクトについての性質を記述し、それに名前をつけることができます。JavaやC#ではあるクラスに特定の実装を強制するものとして使いますが、TypeScriptではその範囲にとどまりません。
クラス化するほどでもない、サーバから受け取るデータについて名前をちょいちょいと与える場合などに使われる場合が多いです。
ECMAScript 2015
さて、それではECMAScript 2015の構文の説明に入っていきます。筆者が育てている4つの個人リポジトリのソースコードを見て、実際に使っているもののみを抜粋してきました。
つまり、ここで紹介している構文は使い勝手がよく、ECMAScript 2015以前よりも改善されている構文である!といえます。
use strict
まずは、 “use strict”; について紹介しておきます。これはECMAScript 5のときからあるディレクティブです。
多くの効能があるため、ここでは詳細には解説しません。ここで解説した理由は、ECMAScript 2015の仕様上、後述のクラスやモジュールの内部は暗黙的にstrict modeになるためです。TypeScriptで書いたコードをコンパイルするとクラスやモジュールの構文も変換されてしまい、暗黙的にstrict modeではなくなってしまいます。
そのため、後々ECMAScript 2015が無変換で動く時代になった時に困らぬよう、今のうちからすべてのファイルの先頭に"use strict";
を書いておき、挙動を揃えておきます。これはトイレに行った後に手を洗うというレベルのエチケットなので、tslintなどを活用して忘れないように気をつけましょう。
let, const
let, constを紹介します。JavaScriptではvar
というキーワードを使って変数定義をしていました。let, constではvarに対して次の違いがあります。
- ブロックスコープである
- 同じ変数名を複数回定義しようとするとエラーになる
- 変数の巻き上げは行われるが宣言前にアクセスするとエラーになる
constの場合、さらに追加があります。
- 宣言時に初期化必須
- 値の置き換え不可
便利ですね。varの不思議な挙動について理解しておかねばならない時代も過去のものとなりそうです。多くの場合で単純にvarをletに置き換えできるし、そうしたほうがよいでしょう。varをletに置き換えて動かなくなるようなコードは、JavaScriptの不快な仕様を利用した悪いコードだと考え、リファクタリングするべきです。
なお、TypeScriptで--target es5
などでコンパイルした時はletがvarに変換されるため、実行時エラーは発生しなくなってしまいます。実用上はコンパイル時にそれと分かればとりあえずは問題ないでしょう。
Classes
オブジェクト指向な言語であればだいたい存在するクラスです。JavaScriptではECMAScript 2015から導入されました。ECMAScript 5までは、prototypeという仕組みを使ってオブジェクト指向なコードを書いていました。
prototypeにも良いところがあり、悪いモノでもないのですが、広く知られているという分かりやすさ、関連する記述がひとところにまとまっているという利点を考えると、やはりclassが使えるというのはありがたいものです。
今後、prototypeの出番は大きく減るでしょう。とはいえ、classの裏側はprototypeであるため、今まで使えたハックも利用可能ではあります。
JavaやC#を見慣れているユーザであれば、大変親しみやすいコードになったと思います。ぜひ、PlaygroundでどういうES5なコードに変換されるかを確認してみてください。これを手書きで書くのは、チーム内の人数が多くなれば多くなるほど辛く感じるでしょう。
また、クラスが使えるようになりましたが、だからといってなんでもかんでもクラスにしなければならないわけではありません。今までどおり、関数や普通のオブジェクトを使うのが適していると思った場合はどんどん使って構いません。無理にクラスを使おうとするほうが、意図のわかりづらい、よくないコードになってしまうでしょう。
TypeScriptのみの独自仕様として、constructorでpublic extention = "!"
のように、アクセス修飾子付きで仮引数を宣言すると、自動的に自分のプロパティとしてセットされます。ECMAScript 2015の仕様に準拠したコードを書きたい場合は、name
プロパティのように、フィールドの宣言をしてからthis.name = name;
のように自分で代入しましょう。
気になる場合は、tscでのコンパイル時に--target es6
を指定してコンパイルし、どのようなpure ECMAScript 2015コードが生成されるかをチェックしてみるとよいでしょう。
Modules
モジュールです。ECMAScript 2015では、ついにモジュールという概念が、JavaScriptの仕様として盛り込まれました。今までのJavaScriptにはモジュールの仕様が正式にはなく、AMDやCommonJSなど、それを埋めるための(ECMAScriptではない)仕様が複数考えだされ、実装されてきました。Node.jsではCommonJSのモジュールに仕様に近いものが採用されています。
ECMAScript 2015でのモジュールの仕様は、ざっくりいうと 1ファイル=1モジュール という仕様です。1ファイル毎に名前空間が分割されていて、どの識別子を外部に公開するかを選択していきます。
モジュールの仕様は、静的に(つまり実行することなく)ソースコード間の依存関係が解決できるようになっています。そのため、今までのJavaScriptのコードとはかけ離れた、全く新しい構文をもっています。
コード例を見ていきましょう。utilA.ts, utilB.ts, main.ts という3つのコードを利用します。3ファイルあるので、モジュールが3つあることになります。
残念ながら、Playgroundで試すことができないので、お手元に環境を作って試してみてください。
exportして、importするだけの仕様ではありますが、慣れるまでは正しい記法がわかりづらく戸惑いを覚えるでしょう。TypeScriptの利点として、動作しない書き方をした場合きっちりコンパイルエラーにしてくれるので試行錯誤する手間が省けます。
Arrow functions
Arrow functions(アロー関数)です。ECMAScript 5までのJavaScriptの関数は、thisの値が他言語ユーザの期待とだいぶかけ離れた動作をしていて辛かったです。アロー関数では期待する挙動と実際の挙動の齟齬がだいぶ軽減されています。
この使い方では、EMCAScript 5の関数と大差ありませんね。タイプする文字数が減って嬉しいですが、まあ、その程度です。名前付きアロー関数は作ることができませんので、素直に変数に代入しましょう。
次の例を見てみましょう。
アロー関数の特徴が顕著に出ていますね。
理由が明確に説明できない限り、今後はfunctionと記述する関数ではなく、アロー関数を使うべきでしょう。具体的に、今までのfunctionを使うのは名前付き関数を作りたい場合や、thisの値を変更するタイプのAPIをもつライブラリを使う場合などです。
Default parameters, Rest parameters, Spread operators
Default parameters(デフォルト値付き引数)、rest parameters(可変長引数)、Spread operators(展開演算子)について取り上げます。これらもECMAScript 2015からの導入になりますが、特にお世話になるのはデフォルト値付き引数でしょう。
ECMAScript 5までは、p = p || "TypeScript";
のような、JavaScriptユーザ以外にはわかりにくいイディオムを取る必要がありましたがだいぶわかりやすくなりましたね。
Template string
Template stringは文字列の生成が今までより簡単にできるようになりました。これにより、文字列を結合してメッセージなどを生成するのが格段に読みやすくなります。Gruntfile.jsやgulpfile.jsでできると、設定ファイルも大変見やすく保守も簡単になります。
まさにテンプレートですね。
Tagged template stringという機能もあるのですが、あまり有効な使いみちがないのと、活用した有名なライブラリなども登場してきていないので入門レベルでは気にする必要はないでしょう。
Promises
ECMAScript 2015から、非同期処理における標準のAPIが定義されました。それがPromiseです。Promiseは「約束する」の名の示すとおり、将来的に処理結果を返すことを約束し、その予約表を先に払い出すイメージです。Promiseの結果は成功か失敗の2状態で表されます。
Playgroundは--target es5
で動作しているため、Promiseの型定義が存在していません。コンパイルエラーが出ていてもRunすることはできますが、できれば手元で試してみるのがよいでしょう。
Promiseは今後さまざまな標準APIの一部として使われることになります。ブラウザ上で動くコードを書いている場合でも、ServiceWorkerやfetch APIなどを使うようになると、まず間違いなく触らなければいけなくなるため、今のうちに確実にマスターしておくことをお勧めします。azuさんのPromise本が大変わかりやすいので熟読すると得るものが大きいと思います。
まとめ
いかがでしょうか。わかりやすく使いやすい変更が多いので、今すぐにでも使い始められると思います。まずは、既存コードなしの100% TypeScriptなコードから書き始め、徐々に適用範囲を広げていってみてください。
CoffeeScriptやECMAScript 5なコードからは遅かれ早かれ移行しなければならないと思いますので、本記事を参考に徐々に実践を始めてみてください!それでは良いTypeScript & ECMAScript 2015ライフを!