ReactのプロジェクトでTypeScriptを選ぶ前に考えるべきこと

May 03, 2018

過去にTypeScriptとReact & Reduxの組み合わせで物を作ったことがあって何も問題のなかった人はこれ読まなくて大丈夫です。
安易に「型つけたほうが〜〜〜イマドキっしょwwww」みたいに煽る記事にイライラしたので書きました。

ちなみに筆者もTypeScriptに挑戦して2週間くらいで手探りなところが多々あるので、何か問題のある箇所や質問したいこと(あるいは仕事の依頼も;))あったら遠慮なく指摘してほしい。Twitterとかで?
(ブログにコメントフォームつけるべきなのか…)

TypeScriptを採用するまえに考えること

  1. あなたはブログ記事のコピペだけに頼らずフロントエンドのビルド環境の構築を行なう忍耐力がありますか

  2. あなたはReact(とRedux)に詳しいですか (「詳しい」と言うのは公式のドキュメントをちゃんと読んだことがありますか程度の意味です)

  3. あなたはTypeScriptと同等かそれ以上に強い型システムを持つ静的型付言語(Scala, Kotlin, Swift, Rust, あともちろんOCamlとHaskell)の知見がありますか

  4. あなたのプロジェクトは時間が潤沢にありますか

最低でも2つにチェックマークをつけれない場合、TypeScriptを採用するのはやめておきましょう。

ただし、時間が潤沢にあって学習のモチベーションが高いメンバー揃いなのであれば挑戦してもいいでしょう。結構面白いですよ。

考え直すべき理由

ビルドプロセスの複雑化

TypeScriptに限らず現代のフロントエンドのビルド環境は複雑です。
TypeScriptのコンパイルはオプションや設定ファイルが多く、設定次第では力を引き出せずに終わります。そして世間の人たちは色々なお気持ちを持っているので、適当に日本語のブログ記事をひとつだけ読んでサンプルをコピペするとババをひく可能性があります。

そしてコンパイル後のモジュールの結合などに結局Webpackなどの力を借りる必要があります。TypeScriptのビルド環境構築には、TypeScriptkコンパイラ + 既存のビルドツール知識の両方が必要になると考えていいでしょう。
TypeScript自体もそうですし、babelやWebpackのバージョンも日進月歩で進歩しています。最低でもREADME.mdを読む忍耐力と時間の余裕がないと最初の一歩でいきなり技術的負債を抱える羽目になるでしょう。

柔軟な型の記述をできることが仇になり、適切なコンパイルオプションを設定しないTypeScriptはただの劣化JavaScriptでしかありません。そして後でそれを修正していくのは 非常に 苦痛です。

ReactとReduxへの知識の要求

「TypeScriptは型が導いてくれるから書くの楽になる!!」なんて考えるのはよしましょう。
ReactやReduxはそもそもJavaScriptで書かれたライブラリです。Reactはシンプルなのでまだ良いのですが、Redux(react-reduxも)の型定義はつじつま合わせで地獄です。もともと知識があって、そこからどう言う型がつくのか逆算するのは簡単ですが、逆は難しいでしょう。

さらに、素のActionCreator -> Action -> Reducerの連携は文字列ベースのアナログなものなので、普通にやると型安全になりません。なので typescript-fsa などの新しいライブラリを導入する必要がでてきます。ここには Type Guard の仕組みなども関わってきます。ただでさえ余計なものをゴチャゴチャと色々入れないといけないReduxですが、TypeScriptをつかうことでそれがさらに増えていきます。

ReactとReduxに十分な知見と自信がないのであればTypeScriptと組み合わせることは、僕はあまりおすすめしません。どうせ白紙から始めるなら、そもそもがTypeScriptで書かれたAngularを使うことを検討してもいいんじゃあないでしょうか。
僕はflowtypeについては十分な知見がないので言及しません。

型システムへの知見

僕の知る限りですが、まずGUIアーキテクチャに知見のあるフロントエンジニアはほとんどいません。そして静的型付け言語の知見がある人はもっと少ないです。オブジェクト指向もおぼつかない人がほとんど(プロトタイプベースにむりやりねじ込んだclass構文は未だに受け入れがたい)ですが、ReactもReduxも作者の趣味からか、明らかに関数型言語を指向しています。

Redux関係ツールは大量のoverloadとgenericsを駆使して型付けされているので、コンパイルエラーが絶望的にわかりにくいです。
初心者は速攻で心が折れて場当たり的な匿名型、あるいはanyやobjectを連発するようになるでしょう。当たり前ですが、これらを使い始めた時点でTypeScriptは記述が多くてめんどくさいだけの劣化JavaScriptです。少々過激な意見かもしれませんが僕はそうおもいます。

また、これは4番目の「時間が潤沢にありますか」に関わる話になりますが、「動作上は問題ないはずなのに型定義的な問題からコンパイルが通らない」みたいな事態がおこりえます。ReactのComponentとElementとNodeの区別をつけてコードを書いている人がどれくらいいるのでしょうか…ぼくは常にうろ覚えです。
そういった定義上のエラー、新しいmiddlewareの導入、ことあるごとに .d.ts をあらためる必要性がでてきます。そして定義ファイルの複雑さに絶望します。

筆者は最初のチェックで1, 2, 3にチェックできる自信がありましたが、TypeScriptを組み合わせた瞬間に生じる作業コストの増大には心底うんざりしましたし、時間がなくてさっさと実装を進めないといけないのに型定義ファイルをチェックしているときは全部anyにしてやろうかとおもいます。
というか僕程度の知見だとanyなしにはいつまでも実装が進まない状況も発生します。もちろん重要なところ(TypeScriptの価値を損ねるようなところ)で型付けをサボるようなことはしませんが、その味付け具合をチームでちゃんと共有できるとはおもいません。

まとめ

TypeScript(他の多くの静的型付けの言語も)は簡単ですが、難しいです。
つまりただコンパイルが通るだけのコードを書き、動くプログラムを書くこと自体にはある意味でそれほどの苦労はありません。型宣言のほとんどを最大限にゆるくすれば良いからです。昔話ですが、筆者は昔C#でコードを書く系のプロジェクトに参加したときにほぼ全ての関数の引数と返り値がObjectで宣言されているものにぶつかったことがあります。
しかしコンパイラの方チェックに最大限の仕事をさせようとすると急に難易度が跳ね上がります。特にJavaScriptが(いまはまだ)あくまで世界の主人であり、従にすぎないTypeScriptには常にanyの引力が存在しています。忍耐力と、それなりの学習コストを投入しないと恩恵を受けるのは難しいでしょう。

一時期HaxeというAltJSに傾倒していたときに感じた、JavaScript製の成果物に型をつけていく徒労感はTypeScriptコミュニティが作ったエコシステムの進歩によってかなり減じましたが、完全には解決していません。
そしてどこの会社も時間がありませんし、現在のWeb製作進行の基本は高速なプロトタイピングとイテレーションだとおもいます。そうでなくとも、例えばそもそも一刻も早く世に出してプロダクトの価値を問いたいスタートアップや、進捗を上司や取引先にせっつかれる中でロジックの実装よりも型合わせクイズに集中できる人がどれだけいるのでしょうか。

僕は結構自分に自信がありましたが、それでもまだTypeScriptだと70%くらいの速度しか出ません。ぼくがツールまわりのセットアップをサボっているのも影響していますが……。
一方で、一度TypeScriptで「ちゃんと」組めば、その後の機能追加や修正は圧倒的に楽かつ安全になるという手応え(知ってたけど)は得ましたし、慣れれば生のJavaScriptと同じかそれ以上の速度で実装を進められるとおもいます。

まとめると、僕がこの記事を通して言いたかったのは「よく考え直して」これだけです。
TypeScriptはその特性がマッチするプロダクトと上手く扱えるメンバーが揃えば強力なツールですが、流行りだからという理由で導入するものではありません。まだね。(JavaScriptにはいい加減うんざりなのでいつかは消えてくれという気持ちはある)

でも最後に一つだけ。型はいいぞ! 色々悲観的なことを言ったけど気持ちと筋力があればなんとかなるからみんな使おうぜ!(雑)