2016年3月29日
全てのJSライブラリはTypeScriptで書かれるべきである
本記事は、原著者の許諾のもとに翻訳・掲載しております。
私はJavaScriptプログラマで、ライブラリをいくつか作っています。しかし最近では RxJS version 5 のために TypeScript を書いています(RxJS version 5は Angular 2 の内部で使われていますが、Angular 2もTypeScriptで書かれています)。現在私は Cycle.js をTypeScriptで書きなおしているところです。
静的型付けと動的型付けの優劣の話はやや議論を呼ぶ話題なので、その議論をすべて再発させるつもりはありません。しかし、私は、 沢山の開発者に使われることを意図した 全てのJavaScriptライブラリはTypeScriptで書かれるべきだと信じています。これはアプリケーションやウェブサイトをTypeScriptで書くということの話ではなく、ライブラリに関する話です、ライブラリはTypeScriptで書かれるべきで、それにはいくつかの理由があります。
よりよいドキュメンテーション
ライブラリは完全に型付きで作ることで、APIの制約や必須条件を明確にすることができます。
JSライブラリによくあることですが、ドキュメンテーションを書くときの後知恵として型があいまいに定義されます。各関数について 「引数はXであると仮定します」 と説明されますが、ずっと徹底されている考えとも限らず、しばしば欠陥があります。ドキュメントに書かれている以外の型を関数に渡してしまったり、ドキュメントに書かれていない不可視の制約がある場合、ランタイムエラーを引き起こします。
Immutable.jsは、TypeScriptで書かれており、そのAPIの型について徹底的に考慮された実に 良い例 です。Reactのドキュメントも、そのAPIの方について丁寧に考慮されています。たとえば これ 。
よりよい自動補完
エディタとIDEには、型定義を使うことで、JavaScriptで書かれたプロジェクトでさえもよりよいコードインテリジェンスと自動補完機能を提供しているものがあります。
少なくとも Visual Studio Code ではそうなっています。
より信頼性の高いライブラリ
あなたが好もうが好むまいが、型はあります。「何もかも」引数に取り「何もかも」出力するAPIはありません。常に前提条件・事後条件があるものです。よいライブラリAPIにはよい制約があるのです。
普通、あるメソッドは文字列/数値/関数/オブジェクトなどを引数に取ります。仮にメソッドが様々な構造のオブジェクトを引数として受けられるとしても、「何が正しいオブジェクトで、何が正しくないのか」という仮定は常にあるでしょう。例えば、全てのプロパティがオプションだとして、その中に payload
, port
, ignore
, host
といったものがあったとします。しかし、これらは通常限られた数の定義済みのプロパティなので、例えば paload
などのタイプミスが無効なプロパティとなります。
型はそこに役立ちます。これらを暗黙上のものにしておくのではなく、明示的にすることができます。インターフェースは有効なオブジェクトの構造を定義するための素晴らしい方法です。
コンパイル時の型チェックは本質的に強力なリンティングツールです。今や、重要でモダンなJSライブラリのほとんどが何らかの方法でリンティングを行っています。型チェックはそれの上位版にすぎません。型という方法で制約条件を与えることで、” 型チェック・リンター “が有効/無効なコードを判別するのを助けるのです。コンパイル時型チェックの説明としてはまったくオーソドックスでない説明の仕方ですが、リンターを使い慣れているJavaScriptマインドな人にはより分かり易いのではないでしょうか。
TypeScriptユーザへのよりよいサポート
ライブラリがTypeScriptで書かれていたとしても、ほとんどの人はJavaScriptライブラリとして利用するでしょう。正直に申し上げて、私自身、たとえライブラリがTypeScriptで書かれていたとしても、日々のベースとしてはあちこちでJSを使い続けるでしょう。
しかし、プロジェクトをTypeScriptで構築することを選ぶ人もいるでしょう。もしあなたが始めから型付きでライブラリを構築すれば、そういったTypeScriptユーザが .d.ts
型定義ファイルを後付けすることなく、よりよく過ごせるようになるでしょう。ライブラリを一からTypeScriptで作ることで、あなたの .d.ts
ファイルがより確実に実際の型制約を反映するようになるでしょう。
ライブラリを Flow で作ってみてはどうでしょう? Flowは素晴らしいもので、TypeScriptとよく似ています。Flowにはおかしいところがなにもありませんし、nullableとnon-nullableの型のアノテーションなど、いくつかの面でTypeScriptに勝るものです。これを書いている時点ではTypeScriptにこの機能はサポートされていません。
nullable/not-nullableのアノテーションは素晴らしいものですが、FlowもTypeScriptもコンパイル時の型チェックをほとんどの型制約に対して提供しており、TypeScriptは先発の利益から少し優位に立っている、ということを述べておきます。そのうちTypeScriptがnullable/non-nullableアノテーションをサポートしてくれることを私は望んでいます。その間は、TypeScriptとFlowの比較を、GitとMercurialのように比較したいと思います。競合する技術の両者がとても似ており、長所短所がそれぞれにあるものの、採択のされ方や、ソフトウェア構築の強固な基盤としての地位の獲得状況の早さにおいて、片方がかなり優位に立っている、というものです。
未来への基盤
型を宣言するやり方は、他にも rtype やFlowType、 PureScript などがあります。TypeScriptがJavaScriptの型定義のデファクトスタンダードになるかどうかを私が言う事はできませんが、少なくともたった今においてはもっとも人気があります。
そして、いくつかの型定義がありますが、型定義がないよりは良いものです。 .d.ts
からFlowTypeの型定義やほかの型定義への転換を構築するのは非常に便利なので、 型付きJavaScript の未来がどうなるにせよ、いま .d.ts
を持っておくことは将来の移行に役立つでしょう。
型付きプログラミングの問題
JavaScriptコミュニティには、” 型は重要ではない “という文化がありますが、「 実質的には型は常に存在しており、 完全に動的なJSコードというものは存在しない」という事を再確認してこれへの結論としたいと思います。JavaScriptでは型は暗示的であり、ドキュメント内でそれが合意されているのです。それを明示化し、ルールとして用いることで、自分たちのコードをより強固にできるメリットがあります。。
私は”型付きプログラミング陣営”からの人間ではありません。最も使ってきたプログラミング言語は、圧倒的にJavaScript(動的)です。Python(動的)は私のお気に入り言語のひとつです。しかし、型は我々の助けになりますし、型がもたらす負の側面らしきものはリントのような警告のみであって、せいぜい幾らかの開発者を「うんざり」させるぐらいのものです。リンティングがコードを良くしてくれることは分かっているので、我々は既にリントを用いています。型はさらに強力で、重要なものです。
もしこの記事が気に入ったなら、あなたのフォロワーに Tweet するのを検討してみてください。
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa