mmuulan
TypeScriptFrontend設計思想

TypeScriptを理解するための設計思想

Created 2026-06-30 · Updated 2026-07-12

目次


概要

このドキュメントは、JavaScriptや一般的なWeb開発の経験はあるけれど、TypeScriptはこれから、あるいは何となく使っている、という人に向けたものです。型注釈の文法(: string の書き方や、ジェネリクスの記法)を一から教えるものではありません。そうではなく、TypeScriptがなぜこういう設計になっているのか、その「考え方の根っこ」を15〜30分でつかむことを目的にしています。

なぜ根っこから入るのか。TypeScriptには「なぜこうなっているの?」と戸惑うルールがいくつもあります。型を書いたのに実行時には効かない、クラスが違っても形が同じなら通ってしまう、any という便利そうな型があるのに使うなと言われる——。これらを一つずつ暗記するのは大変です。でも、おおもとにある一つの価値観さえ分かってしまえば、個別の挙動は「ああ、だからこうなるのか」と一本につながります。AIが書いたTypeScriptコードを見て「この型の付け方はTypeScriptらしくないな」と気づけるようになるのも、この根っこを持っているかどうかで決まります。

そしてその根っこは、突き詰めるとほぼ一つの言葉に集約できます——JavaScriptを邪魔しない。TypeScriptは新しい言語を作ろうとしたのではなく、あくまでJavaScriptの上にそっと乗る「型のレイヤー」として設計されています(出典: TypeScript Design Goals)。この先の各章は、この一つの価値観が場面ごとにどう顔を出すか、という話だと思って読んでください。


そもそもTypeScriptは何を最優先しているのか

新しい言語を学ぶとき、多くの人は「その言語ならではの新しい機能」を期待します。ところがTypeScriptは、少し不思議な立ち位置にいます。TypeScriptがいちばん大事にしているのは、「JavaScriptに余計なことをしない」という自制だからです。

TypeScriptは、Microsoftが「大規模なJavaScriptを、大勢で安全に書けるようにしたい」という動機から作りました。ここで彼らが選ばなかった道が重要です。JavaScriptを置き換える新言語を作るのではなく、既存のJavaScriptをそのまま活かし、その上に型チェックという層をかぶせる道を選びました。だから、動いているJavaScriptコードは、拡張子を変えるだけで基本的にそのままTypeScriptとして通用します(出典: TypeScript for JavaScript Programmers)。

この方針は、公式が掲げる設計目標にもはっきり書かれています。「出力するプログラムに実行時のオーバーヘッドを課さない」「きれいで、読みやすい、見慣れたJavaScriptを出力する」「すべてのJavaScriptコードの実行時の挙動を保つ」——いずれも、JavaScriptの邪魔をしないという一点を向いています(出典: TypeScript Design Goals)。逆に「やらないこと(non-goals)」として、実行時に型情報を持ち込むことや、独自のランタイム機能を足すことを、明確に避けると宣言しています(出典: TypeScript Design Goals)。

これから先の章は、すべてこの「JavaScriptを邪魔しない」という一点が、いろいろな場面でどう形になっているか、という話です。まずこの一本の軸を頭に置いてください。


なぜ型は実行時に消えてしまうのか

TypeScriptを使い始めた人がよく引っかかるのが、ここです。せっかく age: number と型を書いたのに、ブラウザやNode.jsで実際に動くときには、その型情報がどこにも残っていません。「実行中に、この値が本当に number かどうかを型で確認したい」と思っても、それはできないのです。最初は「型を書いた意味がないのでは?」と感じるかもしれません。

これは不具合ではなく、意図した設計です。TypeScriptの型は、コンパイル(正確にはトランスパイル)のときにチェックされ、その後きれいに取り除かれます。出力されるのは、型注釈がすべて消えた、ふつうのJavaScriptです。なぜそうするかといえば、前章の「JavaScriptを邪魔しない」に直結します。もし型情報を実行時まで持ち込めば、そのぶん余計なコードや処理がJavaScriptに紛れ込み、実行時の挙動やパフォーマンスに影響してしまいます。TypeScriptはそれを避け、型は「開発中に開発者を助けるためだけのもの」と割り切りました。公式が「実行時のオーバーヘッドを課さない」「型システムの結果によって異なるコードを出力しない」と明言しているのは、この割り切りの表明です(出典: TypeScript Design Goals)。

この「型は実行時には消える(型消去)」という性質は、とても大事な帰結を生みます。実行時に外から入ってくるデータ——APIのレスポンスやフォーム入力——は、TypeScriptの型だけでは守れないということです。型はあくまで開発時の約束事なので、実行時の検証は別途、自分のコードやライブラリで行う必要があります。ここを誤解すると「型をつけたのに実行時に落ちた」という事故につながります。


型が「名前」ではなく「形」で決まるのはなぜか

JavaやC#に慣れた人がTypeScriptを書くと、ここで強い違和感を覚えます。あるクラスや型として認めてもらうには、明示的に「私はこの型です」と宣言するのが当たり前だと思っているからです。ところがTypeScriptでは、そんな宣言をしていないオブジェクトが、形さえ合っていれば平然と受け入れられてしまいます。

TypeScriptは、値が「何と名乗っているか」ではなく「どんな形(プロパティ)を持っているか」だけを見て型を判断します。これを構造的型付け(構造さえ合えば同じ型とみなす仕組み)と呼びます(出典: TypeScript for JavaScript Programmers)。xy を持つ形を求めている関数には、Point クラスのインスタンスだろうと、その場で作ったただのオブジェクトだろうと、xy さえ持っていれば渡せます。「アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」という、いわゆるダックタイピングの考え方です。

なぜこの方式を選んだのか。理由はやはり「JavaScriptを邪魔しない」です。JavaScriptの世界は、その場で作るオブジェクトや、クラスに属さない自由な値であふれています。もしTypeScriptが「名前で型を判断する」方式(名前的型付け)をとっていたら、そうしたJavaScriptらしいコードのほとんどに型をつけられなかったはずです。形で判断する方式だからこそ、既存のJavaScriptや無数のライブラリに、後から自然に型をかぶせられます。構造的型付けは、JavaScriptの現実に型を合わせにいった結果なのです。


全部に型をつけなくても動くのはなぜか

他の静的型付け言語から来ると、「型をつけるなら、すべての変数にきっちりつけないと動かない」という感覚を持っているかもしれません。TypeScriptはそこがゆるやかです。型注釈を一切書かなくても動きますし、一部だけ型をつけて、残りは後回しにすることもできます。「これは中途半端では?」と思うかもしれませんが、これも狙っての設計です。

TypeScriptは、型を書かなくても、値からできるだけ型を推論(自動で型を推し量ること)してくれます。let name = "Alice" と書けば、明示せずとも name は文字列だと理解します(出典: Everyday Types)。そのうえで、型をどこまで厳密につけるかを開発者に委ねています。まったく型をつけないJavaScriptから、少しずつ型を足していき、最終的にきっちり型で固めるところまで、好きな地点を選べます。この「段階的に型を導入できる」性質を段階的型付けと呼びます(出典: TypeScript and the dawn of gradual types)。

なぜここまでゆるやかにするのか。ふたたび「JavaScriptを邪魔しない」に戻ります。もし「型を全部つけないと使えない」仕様だったら、既存の巨大なJavaScriptコードをTypeScript化する最初の一歩が、あまりに重くなってしまいます。1ファイルずつ、1箇所ずつ型を足していけるからこそ、現実のプロジェクトは無理なく移行できます。前章の構造的型付けも、この「ファイル単位で少しずつ型をつけられる」段階的な導入を支える土台になっています。ゆるやかさは、導入のしやすさのために意図して残された余白なのです。


any があるのに使うなと言われるのはなぜか

TypeScriptには any という型があります。これをつけた値は、どんな操作をしても型チェックに怒られなくなる、いわば「なんでもあり」の型です。便利そうに見えるのに、入門記事でもチームのルールでも、たいてい「any はできるだけ使うな」と言われます。用意されているのに避けろとは、矛盾しているように感じるかもしれません。

これを理解する鍵も、これまでの思想の延長にあります。any は、前章までの「JavaScriptを邪魔しない」という思想が生んだ、避けられない出口です。TypeScriptはJavaScriptの上に乗る以上、「まだ型をつけていない世界」や「型では表現しきれない世界」と必ず地続きになります。any は、その"型のない世界へ抜ける非常口"として用意されています。段階的な導入の途中や、どうしても型がつけられない外部との境界で、逃げ道として機能するわけです。

問題は、この非常口を安易に使うと、型チェックという恩恵をみずから手放してしまう点です。any をつけた瞬間、その値まわりだけTypeScriptの守りが外れ、JavaScriptと同じ「実行してみるまで分からない」状態に戻ります。だからこそ「使うな」ではなく「必要な境界に留め、むやみに広げるな」というのが実務での勘所です。公式も、意図しない any の混入を防ぐ設定(noImplicitAny など)を用意して、開発者が無自覚に守りを失わないようにしています(出典: Everyday Types)。なお、「型が分からない」ことを表したいだけなら、より安全な unknown という型もあります。any が「チェックを全部やめる」のに対し、unknown は「使う前に必ず確認させる」点が違います。安易な any の代わりにこちらを選ぶ、というのが近年よく勧められる考え方です(出典: TypeScript Expert Revision Handbook, dev.to ※コミュニティ記事)。


この思想をAIコーディングにどう活かすか

ここまでの「JavaScriptを邪魔しない」という軸を握っておくと、AIに書かせたTypeScriptコードが「TypeScriptらしいかどうか」を、細かいルールを覚えていなくても見抜けるようになります。AIは膨大なコードを学んでいるぶん、他の言語のクセや、安易な逃げ道をそのまま持ち込みがちだからです。よくあるズレを挙げます。

まず、any の乱用です。AIは「とりあえず動く型」を求めて any を置きがちです。「any は型チェックを手放す非常口であって、常用するものではない」という軸に照らせば、unknown やちゃんとした型に直すべき箇所だと判断できます。

次に、実行時バリデーションの欠落です。APIレスポンスや外部入力を、型注釈をつけただけで「安全になった」かのように扱うコードが出てきたら要注意です。「型は実行時には消える」という軸を思い出せば、外から来るデータには別途、実行時の検証が要ると分かります。

さらに、名前的型付けのクセです。JavaやC#の発想で、「同じ形でも別の型なら区別されるはず」という前提のコードを書いてくることがあります。TypeScriptは形で判断する(構造的型付け)ので、意図的に区別したいなら、それ用の書き方(ブランド型など)が要る、と見抜けます。

最後に、過剰な型注釈です。TypeScriptは型を推論してくれるのに、AIはあらゆる箇所に冗長な型注釈をつけがちです。「必要な型は推論に任せ、書くべきところだけ書く」という姿勢で、読みやすさを保てているかを見ます。

まとめると、レビューのコツは「この型は正しいルールか」を一つずつ照合することではありません。もう一段深く、JavaScriptを邪魔しないという軸に沿っているか、そしてその軸が生む「型は開発時だけの助け」という前提を踏まえているか、という目で見ることです。この軸さえ持っていれば、細かなルールをそらで言えなくても、違和感にちゃんと気づけます。


参考文献一覧