TypeScriptを使った方がいいケースとは?

去年の夏、私たちは大量のコードベース(18,000行以上のコード行数)をJavaScriptからTypeScriptへと変更しました。この移行作業を通じて、両者の相違点や類似点について大いに学び、TypeScriptの優れたユースケースや、TypeScriptを使うべきではないケースなどについて考えてみました。


型システムとは補助輪のようなものです。転倒防止してくれる代わりに、遅くなり、操作性が制限されます。

TypeScriptのユースケース

  1. コードサイズ:ソースコードが膨大である場合、また複数の人がプロジェクトに従事している場合、型システムは明らかなエラーを防ぐのに役立ちます。 特にSPAの場合は当てはまります。誰かが変更したコードが他の人のコードを破損させてしまう可能性があるなら、何らかの安全機構を持つ方がいいでしょう。TypeScriptのトランスパイラは明白な誤りを暴いてくれますし、ある程度賢いものです(もちろん、デバッグの必要性を排除できるということではありません)。ソースコードが巨大でなければ、型アノテーションを追加してコードを長くする意味はおそらくないでしょう。私は180以上のファイルをJavaScriptからTypeScriptに変更しましたが、多くの場合、コードサイズの増加はおよそ30パーセントほどでした。

  2. 過去の経験:チームメンバやあなた自身がC#やJavaのような強い型付き言語のバックグラウンドを持ち、JavaScriptをとことん学ぶ気はないという場合、TSはいい代替案だと思われます。この機会にJavaScriptを学習することをお勧めはしますが、JavaScriptを知らないままTypeScriptを使うことを妨げるものはありません。実際TypeScriptはC#と同じ作者によるものですから、シンタックスは似ています。当社ではC#の開発チームがC#/WPF(デスクトップ界のフロントエンド)で、洗練されたデスクトップアプリケーションをコーディングしていましたが、その後、フルスタックエンジニアとしてWebプロジェクトにアサインされました。彼らにとってはフロントエンドのTypeScriptを学習することは比較的近道であり、バックエンドのC#の知識を生かせるものだったようです。

  3. Babelの代替として:昔Microsoftでは標準の言語(例えばJava)に非標準のプロプライエタリ(例えばJ++)を付加して、開発者に選ばせるということをしていました。TypeScriptは、まさにこれのJavaScript版だと言えます(1996年にはJavaScriptからJScriptを派生させる試みをしていました)。いずれにせよ、ES6はTypeScriptのサブセットであり、TypeScriptのトランスパイラはES5のコードを生成するので、TypeScriptのトランスパイラを使って実質的にES6のコードをES5にトランスパイル(Babelのように)することもできます。これは私見ですが強力なユースケースではなさそうです。TypeScriptのトランスパイラは出力結果としてかなり読みやすいES5のコードを生成するのですが、Angular2チームがGoogle独自のDart言語ではなくTSを選択したのはこれが1つの理由でもありました。他にもTSにはEnumや、コンストラクタでメンバ変数を初期化する機能など、ES6には備わっていないクールな機能があります。私はさほど継承が好きではありませんが、publicprivateprotectedabstractなどのキーワードがクラスに備わっているのは大変便利です。これはTSにはあってES6にはありません。C#の開発者は、メソッドのボディとしてLambda関数を書けることがすごいと言っていました(キーワードthisに伴う頭痛のタネを排除してくれるため)。

  4. ライブラリあるいはフレームワークによる推進:Angular2、またはTypeScriptの使用によって面白くなるようなライブラリを使っている人は、ぜひ使いましょう。アップデート:Angular2を6カ月使ってみた後の開発者たちの感想を読んでみてください。TypeScriptは全てのJavaScriptのライブラリを設定なしで使うことができますが、適切なシンタックスエラーを望む場合は、これらのライブラリの外部に型定義を付加する必要があります。幸運なことにDefinitelyTypedのステキな皆さんがコミュニティ主導のリポジトリとツールを用意してくれました。しかしプロジェクトをセットアップする時は1つ余分なステップとなります(ひと言:JSXのファン向けにはTSXについて説明するべきですね)。

TypeScriptを使わないほうが良いケース

  1. 追加のトランスパイルにかかる負荷: TypeScriptをブラウザでネイティブにサポートする計画は存在しません。(Chromeで実験していたこともありましたが、その後やめてしまいました)。不要なランタイムオーバーヘッドに対して、何か関係があるのでしょう。例えば、誰かが自転車に補助輪を取り付けたいと思った場合、補助輪を取り付けることは可能でも、永続的に取り付けたままにすべきものでもないですよね? つまり、ブラウザ上で実行する前に、常にTypeScriptのコードをトランスパイルしなければならなくなります。標準的なES6とは全く別次元の話です。ES6がほとんどのブラウザでサポートされれば、現在行っているES6からES5へのトランスパイルは不要となるでしょう。JavaScriptにおいてES6は最大の変化であり、ほとんどのプログラマが利用するようになると思います。しかし、プログラマが次バージョンのベータ版を使いたいと思った場合や、全てのブラウザには実装されていない機能を利用する場合は、どちらにしてもトランスパイルすることになります。トランスパイルしないとすれば、ファイルを修正しブラウザをリフレッシュするだけです。それが全てであり、監視したりオンデマンドでトランスパイルしたりシステムをビルドしたりする必要はありません。TypeScriptを選ぶとすれば、JavaScriptのライブラリやフレームワークのために(DefinitelyTypedを使ったり独自の型アノテーションを書いたりしながら)型の定義を記録する羽目になります。このようなことは、ピュアなJavaScriptを使っているプロジェクトでは、必要のない作業です。

  2. 変わったデバッグのエッジケース: SourceMapを使うとTypeScriptのデバッグが簡単になりますが、そのまま使うには不完全な上に、面倒で分かりづらいエッジケースがあります。実際には実行しているコードがないにもかかわらず、コードの特定の行を実行しているとブラウザが判断してしまうというようなことです。また、thisというキーワードやそれに付随するプロパティ(ちなみに、大抵の場合、_thisは問題なく動きます)をデバッグする際に問題が発生するパターンがあります。この問題が起こるのは、現状のSourceMapに、変数に対する十分なサポートがないためです。(ただし、将来的にこの状況は変わる可能性があります)。

  3. パフォーマンスペナルティ: 我々のプロジェクトでは、9,000行以上の古き良きES5 JavaScriptのコードがありました。WebGLによる3Dのcanvasに動力を与えるだけのものでしたが、我々はその方法をそのまま使っていました。TypeScriptのトランスパイラ(Babelと同じようなもの)の機能には、特別なコード(継承やEnum等)を生成する必要のあるものがあります。どんなにトランスパイラが素晴らしくても、腕の良いプログラマの最適化レベルを上回ることはできません。そのため、我々はそのままのES5を維持することにしました。デバッグやデプロイを簡単にするためです。(何があってもトランスパイルはしません)。そうは言っても、多くのプロジェクトにとっては、型システムやよりモダンなシンタックスの利点に比べれば、パフォーマンスペナルティは恐らく取るに足らないものでしょう。しかし、ミリ秒が問題になる場合もあり、その場合はどんな種類のトランスパイルも推奨できません(BabelやCoffeeScriptやDart等でさえもです)。一方で、TypeScriptは実行時の型チェックのためにコードを加えるようなことはありません。型チェックは全てトランスパイルする時に行われ、生成されたJavaScriptのコードからは型アノテーションが削除されます。

  4. 迅速性: 何かを設定する時はJavaScriptを使った方が早くできます。型システムでないことで、非常に軽く、内容を簡単に変更することも可能です。もちろんコードは簡単に壊れてしまうものでもありますが、自分が何をしているのかが分かっていれば、JavaScriptの方が柔軟性を備えていると言えます。型システムのユースケースの中には、コードを壊れにくくするということがあるのを思い出してください。例えるならTypeScriptはWindowsで、JavaScriptはLinuxのようなものです。JSの世界には、型システムの”補助輪”はありませんし、自分が何をしているか分かっているのが当然とされます。しかし、より速く、より簡単に操作できます。特にプロトタイプ制作の段階にいるのなら、TypeScriptで時間を無駄にしないでください。JSはより機敏で融通がきくからです。TSはJSのスーパーセットです。つまり、後から簡単にJSをTSに変換できるのです。もしも型付き言語を使っているのなら、IDEでコードを書く/最適化するのも速くなるかもしれません。型システムは機械が構文を理解するのを手助けするからです。

私自身の個人的な好み

全てにとって最良の言語というものはありませんが、特定のプロジェクトにおいては、それに適した言語やライブラリ、フレームワーク、データベース、OS、その他諸々があります。

私たちのプロジェクトには、TypeScriptを使うのが理に適っていました。私は趣味で行っているプロジェクトのうちのいくつかをTypeScriptにリファクタリングしようとしましたが、それはとても大変でした。個人的には、TSに関して気に入っているのは、以下に挙げる4つのことです。

  • ES6と完全に互換性があること。Microsoft製品が他のブラウザでもきちんと機能するのを見るのは気分がいいものです。私たちのエコシステムはGoogle、Mozilla、Apple以外の強いライバルから恩恵を受けることができます。Microsoftはこれに、かなりの労力を注ぎ込んでいます(例えば、全てのプラットフォームのGoogle ChromeでTSを使ってVisual Studio Codeを一から書く、ということまでしています!)。

  • 型システムはオプショナルなものですです。私のバックグラウンドはC言語やJavaだったので、JSに型システムがないことを「開放的」だと思いました。しかし、実行時の単純でくだらないバグのせいで時間を無駄にしたくありません。TSによって、多くのバグと、実に難しいバグによって時間を浪費することを回避できます。いいバランスで、これが好きなのです。私に合っています。私はいつでも、可能な限り型を使います。心に平穏をもたらしてくれるからです。しかし、これは私の個人的な感想です。私はTSを使うなら、ES6の限られた機能で自分に制限を課したくないのです。

  • トランスパイラのアウトプットは、かなり読みやすいです。私はSourceMapのファンではないので、生成されたJavaScript上でデバッグしますし、それはとても素晴らしいものです。なぜAngular 2がDartよりもTSを選んだのか、納得です。

  • ツールはとても優れています。WebStormは、JavaScriptの扱いに長けています(最も賢いJSのIDEはこれだと言う人もいるでしょう)。しかしTypeScriptは、できることの幅を全く新しいレベルにまで広げてくれます。オートコンプリーションとリファクタリング機能は、(VSCodeにおいて)かなり正確に機能します。でもそれはIDEが特別賢いからというわけではないのです。

で、結局?

TypeScriptは全てを解決するものではありません。これを使っても、ひどいコードを書く可能性はあります。TSを嫌う人たちは、変化に対する恐怖のせいで、あるいは知り合いの知り合いが変化を恐れているせいで嫌うのでしょう。それでも地球は周り、TSはどのみち、コミュニティ新しい機能を導入します。しかしReactと同様に、私たちの業界の境界をプッシュする有力なテクノロジの1つです。そしてあなたがこれを使おうと使うまいと、あなた自身の考えを発展させるために使ってみることは害にはなりません。学習曲線はありますが、JSに馴染みがあれば学習もスムーズに進むでしょう。

等価なJSのコードの例が見られるオンライン上のリアルタイムのトランスパイラもあります。

こちらは簡単なチュートリアルと、とても優れたガイドです。しかし私自身はどちらかと言えば言語リファレンスを好むタイプの人間です。もしも動画の方がお好みなら、Udemyのコースもあります。

John Papaによる、ES5とTSに関する簡潔な記事もあります。

おまけ:programming is the best job everも読んでみてください。