私がTypeScriptについて勘違いしていたこと、そしてその理由

(訳注:2016/9/28、頂きましたフィードバックを元に記事を修正いたしました。)

何か新しいものが登場したと聞くと、人はそれに対する賛否を選ぶ傾向があります。TypeScriptが登場したときの私は、キーコンセプトのうち自分に合わないものをほんの幾つか取り上げて「否」の側を選んでしまいました。この記事では、私が当時どのように考えていたか、そして私がどのようにして「TypeScriptの背景には、大きな犠牲なしに利点を得る方法を知る人々による偉大な考えがあった」ことに気付いたかを説明しようと思います。

TypeScriptが登場したときの私の考え

Anders Hejlsbergが何かに取り組んでいるときは、つい私はそちらに注意を完全に傾けてしまいます。彼はコンパイラの構築やプログラミング言語の設計を30年近く経験してきています。彼の様々なプログラミング言語に関する貢献については彼のWikipediaのページでもっと読むことができます。

「彼がJavaScriptにトランスパイル(つまり、ソースコードからソースコードへのコンパイル)される言語に取り組んでいる」と聞いた時、私の第一印象は失望でした。MicrosoftはDart/CoffeeScriptの道をたどり、ECMAScriptの標準の道を拒んだのだと。私は何かを短く書くためだけに、新しい言語を学んでこれまで学んだことを忘れてしまうようなことはしません。もっとメリットがないと、そうする理由がありません。

また、Microsoftの「ASP.NET Webフォームを導入することでWindowsの開発者をWeb開発に取り込もうとした」というやり方について、私には悪い思い出もありました。Webフォームは、Webの中核となる技術を、混在した結果とともに抽象化してしまいました。「TypeScriptは、JavaScriptを”学べない”C#開発者を対象にしたものなのではないか?」「新しい言語は、C#の開発者が使い慣れた機能を持つものになるのではないか?」

Hejlsbergがチームにいてさえも、私はTypeScriptに単純に歓喜することはできず、それ以上深く探ることはしませんでした。その言語とコンパイラに関するいくつかのキーポイントを完全に見落としていたのです。

TypeScriptのメリットを今すぐ得る

最近のクライアントのプロジェクトで、TypeScriptがReactReduxと共に使われていました。ReactとReduxは共に私にはなじみがあるもので、モダンなJavaScript (ECMAScript 2016, ES6) も6か月ほど書いていました。

TypeScriptを始める方法の一つは、お気に入りのエディタを一つ選び(例えばVisual Studio CodeSublime Text、あるいはAtom)、そこからTypeScriptのコンパイラを実行することです。私はTypeScript Sublime Pluginをインストールし、ネイティブHTML要素での幾つかの自動補完サジェストを試しました。

技術的な注記:テキストエディタのプラグインは、TypeScriptコンパイラのAPIの一部であるLanguage Serviceを利用します。これは、開発者にとって低レイテンシでのフィードバックが重要である場合のテキスト編集のために設計されています。上記のアニメーションの例のgetCompletionsAtPositionなど、美しいAPIを提供しています。

TypeScriptとサードパーティのライブラリ

ここまでまだTypeScriptをまったく書いておらず、.ts拡張子を持つES5のソースコードファイルを書いただけです。サードパーティライブラリのAPI記述を入手することで、コンパイラからもっと多くの利点を得られます。API記述をTypeScriptではType Definition(型定義)と呼びます。

有名なViewライブラリであるReactと、その型定義をダウンロードしてみましょう。型定義ファイルは、react.d.tsのように、ファイル名の”d”から判別できます。

型定義を管理するツールはTypingsといいます。

Typingsをインストールするには、npm install typings -gを実行します。

TypingのコマンドラインAPIが馴染みのあるものであることに気付くのではないでしょうか。npmで用いられるものによく似ているのです。

Typingsをインストールした後は、initコマンドを用いて設定ファイルを生成します。 : typings init

searchコマンドを–nameフラグと共に使い、型定義を名前で検索しましょう。

> typings search --name react
Viewing 1 of 1

NAME  SOURCE HOMEPAGE                         DESCRIPTION VERSIONS UPDATED
react dt     http://facebook.github.io/react/             2        2016-05-26T13:46:01.000Z

“dt”は型定義のソースの場所を表します。これは例えば、npmであったり、Definitely Typed(dt)という有名なサイトだったりします。ソースのリストはここで見られます。

型定義はinstallコマンドでインストールできます。

typings install dt~react dt~react-dom --save

–saveフラグはnpmの–saveフラグと似た挙動を持ちます。つまり、型定義の参照を、typings initで生成されたtyping.jsonに保存します。

まだReactそのものをインストールしていませんでしたが、npmでインストールできます。

npm install react react-dom --save

サードパーティのライブラリとその型定義ファイルがある状態での開発とは、一体どのようなものになるのでしょう?私は例として、2つのプロパティを持つReactコンポーネントを作り、それを使ったり編集したりする際に自動補完がどのように動作するかを観察しました。

alt-text

この例では、TypeScriptの最初のスニペットとしてインターフェース宣言を追加しています。私はあらゆる場面で型を追加することに厳格ではありませんが、外部に露出するエントリーポイント(コンポーネントのプロパティなど)を説明することは有益です。

型定義のメリット

これで、APIの定義を覚えたり覗いたりする必要がなくなり、APIを利用するのが簡単になりました。リファクタリングももっと簡単になります。

ここで、他のたくさんのコンポーネント内で使われていて、異なるプロパティが必要なDemoComponentがあると仮定しましょう。例えば、nameプロパティがあり、より特化した他のプロパティ(username, surname, fullNameなど)に変更されうるとします。大きなプロジェクト内で’.name’というキーワードで検索・置換を行うと、端的に言って、あなたは参ってしまうでしょう。正しいコンテキストかどうかについて、検索でヒットしたものをすべてチェックしなければなりません。型が適切な箇所で用いられていれば、nameプロパティはより語義に忠実な意味になり、さらに、ツール側にもリファクタリングで影響される名前がどれであるかわかるようになります。

alt-text

上記のアニメーションで示した例はシンプルですが、大きなコードベースでも同じような操作ができることは容易に想像できるでしょう。

型に関する誤解

“強い型付け”と聞くと、「古い(型インターフェースなど以前の)C#やJavaのコードのように、あらゆる箇所で型が必要になる」と考えられがちです。

IDictionary<int, Car> carsById = new Dictionary<int, Car>();

これでは、利点がないのに多くのノイズがあります。

TypeScriptには型推論という機能があり、以下のように入力するとエラーを吐くように動作します

let name = "tatu"
name = 2; // an error

nameがStringであることが推論されているため、明示的に示す必要がないのです。

また、TypeScriptにはContextual Type(文脈的型付け)という機能があります。以下はドキュメントからの例です。

window.onmousedown = function(mouseEvent) {
  console.log(mouseEvent.buton);  //<- Error
};

mouseEventには定義された型はありませんが、それでもTypeScriptはmouseEventbuttonというプロパティを持たない場合にエラーを投げることができます。どうしてこれが可能なのでしょう?

それは、TypeScpritが「onmousedownに対するコールバックはMouseEvent型の引数をとる関数である」と知っているからです。MouseEvent型はbuttonプロパティを持ちません。こういったチェックにより、開発者の時間を節約することができます。

TypeScriptを他のツールと共に使う

JavaScriptの開発で使ってきた他のツールについて考えたとき、思い至ったのは以下のものでした:リンティングツール、Browserify/Webpack、テストフレームワーク、そしてBabelJSです。これらのツールは、TypeScriptによって時代遅れになるでしょうか?

lintツール

エラーの捕捉においてTypeScriptがとても強力であれば、果たしてESLintやJSHintなどのツールは必要でしょうか?答えは「Yes」です。例えば、エラーのチェックに加え、ESLintはベストプラクティスや文体といった面でのコードフォーマットのチェックを行えます。残念ながらESLintとの互換性はTypeScript ESLint Parserプロジェクトに依存しており、このプロジェクトはまだ実験段階です。

良いニュースとしては、TypeScriptのために作られたTSLintというツールが存在します。

Browserify/Webpack

TypeScriptはバンドルツールではないため、例えば「自分自身のコードはTypeScriptで書いているが、ブラウザではサードパーティ製ライブラリであるlodashを使っている」というケースで、TypeScriptではlodashをフェッチして1つのファイルにバンドルすることができません。バンドルするのはBrowserify/Webpackの役割になります。

テストフレームワーク

私はTypeScriptでテストを書き、JavaScriptにコンパイルし、テストの実行などのためにMochaを使う、ということをやりました。これに関しての皆さんの意見を聞きたいです。

BabelJS

TypeScriptがES2016の機能のサポートしているということは、BabelJSはもう不要でしょうか?答えは「状況による」です。賢い人に、BabelJSのもたらす利点について尋ねてみました。


著者:「ES2015のコードをトランスパイルするのに、#TypeScript コンパイラではなく#babeljs を使わなければいけない理由を、誰か教えてくれませんか?」
Tero Parviainen:「TypeScriptは今のところasync/awaitをES5にコンパイルできません。もしこれらが必要であればBabel等を使う必要があります」

言い換えれば、TypeScriptはasync/awaitをサポートしているが、それを全ブラウザで理解可能なECMAScriptにコンパイルすることはできません。

この機能を使わないのであれば、ビルドプロセスからBabelJSを消してしまっても構わないのです。

追記:読者のBirk Skyumが指摘してくれたのですが、この機能のES3へのサポートはTypeScriptのロードマップに存在しており、バージョン2.0でリリースされる予定とのことです。

TypeScriptを使うべき時とは

TypeScriptに関するブログ投稿のほとんどは、「TypeScriptは大きなプロジェクトでの使用を企図している」と言及しています。Anders HejlsbergのJavaScript Jabberでのインタビュー(30:50)によると、

もし5行だけのコードを書くのであれば、それはTypeScriptの導入という努力には値しないでしょう…… プロジェクトが大きくなればなるほど、TypeScriptの有用性は上がっていきます。さきに私が述べたように、数千行のコードになるまでには、スラム・ダンク(大成功)になっているでしょう。

* 「スラム・ダンク」がわからず、Google検索しなければなりませんでした。どうやら、外すほうが難しいような簡単なシュートを指すバスケット用語のようです。

結論

願わくば、少なくとも私のTypeScriptへの熱狂が皆さんに伝わり、それで皆さんがTypeScriptにもう一度チャンスを与えるようになれば幸いです。実際のプロジェクトでTypeScriptを使って成功したか、あるいは惨めな経験をしたか、是非お聞きしたいです。それではまた今度!

Discuss on Hacker News