GUIDE / 学習の意思決定 / 更新 2026-10-31

TypeScriptは「書ける」の次、型設計をどこまでやるか

TypeScriptは「書ける」の次、型設計をどこまでやるか

TypeScriptの学習には、わかりやすい達成目標がある。anyを消すこと、赤い波線を消すこと。コンパイラが黙ったら「書けるようになった」と感じて、そこで足を止める人は多い。だが実務でTypeScriptの価値が分かれるのは、その先にもう一段ある場所だ。「型で、不正な状態をそもそも表現できなくする設計」までやるかどうか。これは「書ける」の延長ではなく、別のスキルだ。

anyを消すのは守り、型設計は攻め

anyを消していく作業の本質は、すでに書いたコードに対してコンパイラが出す赤線を、一つずつ潰していくことだ。順番としては常に「コードが先、型注釈が後」になる。何かが起きた後に、それを型という後付けの説明でなぞっているにすぎない。これは実行時エラーを事前に拾う、守りの技能だ。守りとしては確かに有効で、anyが多いコードベースよりは事故は減る。

型設計はこれと順番が逆になる。先に「この値は、ありうる状態としてどこまでの幅を持つか」を決めて、その幅の外にある組み合わせを、型として表現できないようにしてから、コードを書き始める。例えばログイン前とログイン後でユーザー情報の形が違うなら、ひとつの大きな型にoptionalなフィールドを並べるのではなく、判別可能なユニオン型(discriminated union、状態を示すタグ付きのフィールドで分岐する型)として分けてしまう。そうすると「ログイン前なのにユーザーIDだけ存在する」という、本来ありえない組み合わせが、実行時のif文のミスではなく、コンパイルの時点で表現できなくなる。バグを直す技能ではなく、バグが起きる場所を設計図から消す技能、という違いがここにある。

「ありえない状態」を型にする、もう一歩

この一段をやるかどうかで差が出やすいのが、値の意味をIDで混同する事故だ。ユーザーIDと注文IDがどちらもstringだと、関数の引数を取り違えても型チェックは通る。ここでブランド型(本来のプリミティブ型に、人工的な識別用のプロパティを足して型レベルだけで区別する技法)を使い、UserIdとOrderIdを型として別物にしておくと、取り違えた瞬間にコンパイルエラーになる。実行時の値は変わらずただのstringのままで、コストはほぼゼロに近い。これが、型設計に投資したときに得られる典型的なリターンだ。

ただし、ここには深掘りの罠もある。ユーティリティ型を組み合わせて凝った型を作ることが自己目的化し、誰も読めない型定義のパズルを量産してしまう「型体操」は、チーム開発ではむしろ負債になる。線引きは単純だ。型を見た瞬間にチームの誰もが「どんな状態がありえて、どれがありえないか」を読み取れるなら投資の価値があり、型を理解するために型だけを読み解く時間が発生するなら、それはもう型設計ではなく型パズルだ。

全員が凝った型を書けるようになる必要はない。だが「書ける」で止まっている人が、判別可能なユニオン型とブランド型という二つの道具だけ持って、よく使う値の「ありえない状態」を一つ型で締めてみる。その一段だけで、バグが起きてから直す側から、起きる前に消す側へ、少しだけ立ち位置が変わる。

次に手を伸ばすなら、TypeScriptの型をどこから固めていくか、Reactの状態管理と組み合わせてどこまで広げるかを、それぞれの学習データで見ておくと役に立つ。

https://s-tasklog.com/learn/typescript

https://s-tasklog.com/learn/react

tasklogは、こうした学び方の優先順位に迷ったときの手がかりとして、実際に読まれ、引用されている技術書や学習記事を集めて置いている場所だ。

スポンサーリンク

関連データ

ほかのガイド