1. はじめに
Javaを書いていると、フロントエンドの話でTypeScriptの名前をよく聞きます。JavaScriptに型を付けただけ、というのが最初の印象で、なぜここまで選ばれるのかはピンときていませんでした。調べるだけでなく、小さなコードを書いて、自分の手で確かめてみることにしました。
コードの下書きと情報の調査にはAIを使用しています。動作確認は、筆者自身が実際に実行して行っています。
2. 何を確かめたか
「型があると何がうれしいのか」を、次の3つに分けて考えました。
- どんな間違いが、実行前に見つかるのか
- それはいつ、どこで知らせてくれるのか
- 逆に、見つからない間違いは何か
この3つを確かめるために、4つの実験を用意しました。
| 実験 | 確かめたいこと | 選んだ理由 |
|---|---|---|
| 1. 型の食い違い | 変数や引数の型の違いが、実行前に見つかるか | 型チェックのいちばん基本の働きだから |
| 2. null |
null かもしれない値の扱いが指摘されるか |
Javaでも身近なエラーで、比較しやすいから |
| 3. 打ち間違いと必須項目の漏れ | オブジェクトの形の間違いが見つかるか | 実際のコードでも起こりそうな間違いだから |
| 4. 型は実行時を保証しない | 型チェックを通った値が、実行時も型どおりとは限らないか | 型でできないことも知っておきたかったから |
実験1〜3は「見つかるもの」を、実験4は「見つからないもの」を確かめるための実験です。
3. 前提
3-1. TypeScriptとは
MDNでは、TypeScriptを「JavaScriptに静的な型チェックを加える言語」と説明しています。JavaScriptのスーパーセットで、実行時の挙動はJavaScriptと同じです。型のチェックはコンパイル時に行われ、コンパイルが終わると型の注釈は取り除かれて、出力はただのJavaScriptになります。1
「型チェックはコンパイル時だけ」という点が、実験4の話につながります。
3-2. 環境
以下は実際に使った環境です。
| 項目 | バージョン |
|---|---|
| OS | windows |
| Node.js | v22.17.0 |
| TypeScript | 7.0.2 |
3-3. 準備
作業用のフォルダを作り、TypeScriptをインストールします。
mkdir ts-experiment
cd ts-experiment
npm init -y
npm install -D typescript
npx tsc -v
npm install -D typescript の結果です。
added 2 packages, and audited 3 packages in 2s
found 0 vulnerabilities
npx tsc -v の結果です。
Version 7.0.2
4. 動かして確かめる
ファイルはすべて ts-experiment フォルダの直下に置きます。実験1〜3は、次のコマンドで型チェックだけを行います。
npx tsc --noEmit --strict ファイル名.ts
--noEmit はJavaScriptを出力しないオプション、--strict は厳しめの型チェックをまとめて有効にするオプションです。
4-1. 実験1: 型の食い違い
文字列の変数に数値を代入するケースと、number の引数に文字列を渡すケースを試します。
exp1.ts
let userName: string = "Taro";
userName = 123;
function add(a: number, b: number): number {
return a + b;
}
add("1", 2);
npx tsc --noEmit --strict exp1.ts
結果です。
exp1.ts:2:1 - error TS2322: Type 'number' is not assignable to type 'string'.
2 userName = 123;
~~~~~~~~
exp1.ts:8:5 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
8 add("1", 2);
~~~
Found 2 errors in the same file, starting at: exp1.ts:2
エラーは2つ出ました。2行目の代入(TS2322)と、8行目の引数(TS2345)です。メッセージには、どの型をどの型に渡せないのかが書かれていて、行番号と列番号、~ で場所も分かります。
ここまで、コードは一度も実行していません。エディタでも、保存する前から該当の箇所に赤い波線が出ていました。
4-2. 実験2: null
string | null の値に、そのまま .length を使います。
exp2.ts
function getLength(value: string | null): number {
return value.length;
}
console.log(getLength("abc"));
npx tsc --noEmit --strict exp2.ts
結果です。
exp2.ts:2:10 - error TS18047: 'value' is possibly 'null'.
2 return value.length;
~~~~~
Found 1 error in exp2.ts:2
エラーは1つで、2行目の value が「null かもしれない」と指摘されました。null でないことを確かめずに .length を使うと、コンパイルの時点で止められます。指摘が出たのは関数の中の value.length で、getLength("abc") の呼び出しには出ていません。
4-3. 実験3: 名前の打ち間違いと、必須項目の漏れ
interface のプロパティ名を打ち間違えたケースと、必須項目を書かなかったケースを、1つのファイルで試します。
exp3.ts
interface User {
id: number;
name: string;
email: string;
}
// プロパティ名の打ち間違い (name → nme)
const user1: User = { id: 1, nme: "Taro", email: "taro@example.com" };
// 必須項目 (email) の漏れ
const user2: User = { id: 2, name: "Hanako" };
npx tsc --noEmit --strict exp3.ts
結果です。
exp3.ts:8:30 - error TS2561: Object literal may only specify known properties, but 'nme' does not exist in type 'User'. Did you mean to write 'name'?
8 const user1: User = { id: 1, nme: "Taro", email: "taro@example.com" };
~~~
exp3.ts:11:7 - error TS2741: Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'User'.
11 const user2: User = { id: 2, name: "Hanako" };
~~~~~
exp3.ts:4:3 - 'email' is declared here.
4 email: string;
~~~~~
Found 2 errors in the same file, starting at: exp3.ts:8
エラーは2つ出ました。
1つ目は8行目の打ち間違いです。nme は User にないと指摘され、Did you mean to write 'name'? と、直す候補まで出ました。位置は、間違えた nme の部分です。
2つ目は11行目の必須項目の漏れです。email が足りないと指摘され、位置は代入先の user2 でした。email が宣言されている4行目も一緒に示されるので、定義に戻って確かめられます。
どちらも、interface の定義と照らし合わせて見つかっています。
4-4. 実験4: 型は実行時の値を保証しない
外から入ってくる値を想定して、JSON.parse(...) as Repo で、型と違う値を入れます。数値のはずの stars を、文字列にしてあります。まず型チェックだけを行い、次にJavaScriptへコンパイルして実行します。
exp4.ts
interface Repo {
name: string;
stars: number;
}
const json = '{"name":"sample","stars":"10"}';
const repo = JSON.parse(json) as Repo;
console.log(repo.stars + 1);
npx tsc --noEmit --strict exp4.ts
型チェックの結果です。
(何も出力されなかった。エラーなし)
続けて、コンパイルして実行します。
npx tsc --strict exp4.ts
node exp4.js
実行結果です。
101
型チェックはエラーなしで通りました。ところが実行すると、repo.stars + 1 は 11 ではなく 101 になりました。stars が数値ではなく、JSONの中の文字列 "10" のままだったので、足し算ではなく文字列の連結になったのだと思います。
as Repo は型アサーションと呼ばれるもので、JSON.parse の結果を Repo として扱わせるだけです。中身が型と合っているかは確かめません。型チェックを通ったことと、実行時の値が正しいことは別だと、手元で確かめられました。
5. 公開情報から見た有用性
実験とは別に、公開されている情報も見てみました。
5-1. 実行前にエラーを見つけられる
MDNは、JavaScriptでは実行時にしか見つからないエラーを、コンパイル時の型チェックで検出できると説明しています。1 実験1〜3で見たのは、この点です。
5-2. リファクタリングとエディタの補完
Vueの公式ドキュメントは、型を使うと一般的なエラーを検出できて、本番での実行時エラーを減らせること、大規模なアプリでも安心してリファクタリングできること、IDEで型に基づく補完が効くことを挙げています。Vue自身もTypeScriptで書かれていて、公式パッケージには型定義が同梱されています。2
5-3. 利用状況
Stack Overflowの調査で、TypeScriptを「過去1年に本格的に使った」と答えた人の割合です(全回答者)。2026年は、使われている言語の6位でした。3
2018 █████████ 17.4%
2023 ███████████████████ 38.9%
2025 ██████████████████████ 43.6%
2026 ██████████████████████ 43.8%
(█ = 約2ポイント)
GitHubの調査では、2025年8月に、TypeScriptが月間の貢献者数でPythonとJavaScriptを抜き、初めて最も使われる言語になりました。ただ、他の指標ではJavaScriptやPythonが上位のものもある、とも書かれています。4
6. 判断が分かれそうな点
実験と調べた内容を合わせると、型を導入するときに迷いそうな点が3つありました。
6-1. 型チェックをどこで走らせるか
Vueの公式ドキュメントによると、Viteベースの構成では、開発サーバーもバンドラーも型チェックをしません。型の確認は、IDEと、コマンドラインの vue-tsc で行う形が案内されています。2
| 方針 | 利点 | 気をつけること |
|---|---|---|
| IDEだけで確認する | 書いた直後に分かる | エディタを見ていない人や、CIでは気づけない |
| コマンドラインでも確認する | 手元の環境に左右されず、同じ基準で確認できる | 実行する手順を決めておく必要がある |
「ブラウザで動いた」ことと「型チェックを通った」ことは別です。チームで開発するなら、後者の仕組みも決めておく必要がありそうです。
6-2. 外から入ってくる値をどう扱うか
実験4で見たとおり、as(型アサーション)は値をその型として扱わせるだけで、値の検査にはなりません。型の注釈はコンパイルで取り除かれるので、実行時には何も残らないからです。1
| 方針 | 利点 | 気をつけること |
|---|---|---|
as でその型として扱わせる |
書くのが簡単 | 実行時に値が違っても気づけない |
| 実行時にも値を検査する | 型と違う値を入口で止められる | 検査のコードを書く手間が増える |
6-3. 既存のJavaScriptをどう移行するか
大きなコードベースでの例もあります。Airbnbは移行ツール ts-migrate を公開していて、一度に全体を移行するほうが、ファイルごとに少しずつ移行するより良いと考えています。社内の分析では、バグの38%がTypeScriptで防げたと見積もられています。実測ではなく、過去の障害の振り返りに基づく見積もりです。5
| 方針 | 利点 | 気をつけること |
|---|---|---|
| 一度に全体を移行する | 型が付いていない部分が残らない | 一度の作業量が大きい |
| ファイルごとに少しずつ移行する | 少しずつ進められる | 型が付いた部分と付いていない部分が混ざる期間が長くなる |
7. 考えたこと
素のJavaScriptでは、TypeScriptのような静的型チェックを標準では行わないため、今回の実験1〜3のような間違いを、実行する前に防げない可能性があると思いました。同じコードをJavaScriptで試したわけではありませんが、MDNも、JavaScriptでは実行時にしか見つからないエラーを、TypeScriptは実行前に見つけられると説明しています。1 たとえば実験1の add("1", 2) や実験2の value.length は、実行する場面になって初めて気づく形になりそうです。
Javaは、コンパイルの時点で型を確かめてくれる言語なので、その感覚に近いものがJavaScriptの世界にも持ち込めるのだと思いました。
ただ、実験4で見たとおり、型を付けても、外から入ってくる値までは防げません。型があれば安心というわけではなく、防げる間違いと防げない間違いがあることを知ったうえで使う必要があると感じました。
8. まとめ
TypeScriptの型は、間違いを早く見つけるための仕組みであって、間違いが起きないことを保証するものではない。実際に動かしてみて、そう感じました。
- 型の食い違い、
nullの可能性、プロパティ名の打ち間違い、必須項目の漏れは、コードを実行する前に、場所つきで指摘された(実験1〜3) - 指摘のメッセージには、
Did you mean to write 'name'?のように、直す候補まで出るものがあった - 型チェックを通っても、外から入ってくる値は、実行時に型と違うことがある。
asは検査ではなく、実験4では101になった - Viteベースの構成は、型チェックをしない。IDEやコマンドラインで確認する仕組みを、別に決めておく必要がある
公開情報を見ても、TypeScriptは、Vueのように公式にサポートされ、利用も広がっています。型があれば安心というより、見つけられる間違いと、見つけられない間違いの境目を知ったうえで使うものだと思いました。
同じようにJavaからTypeScriptを見る方の参考になれば幸いです。