対象: プログラミング初心者〜中級 / JavaScript経験者歓迎
ねらい: TypeScript(TS)が「なぜ生まれ」「何が便利で」「Javaなど名目型の言語とどう違うか」を、図とコード例で一気に理解
0) 結論(TL;DR)
- TypeScript は 「大規模でも壊れにくい JavaScript を書くための、型とツールの拡張」。
- JS の上位互換なので、TS で書いたものは コンパイルして普通の JS になる(= 型は実行時に消える)。
- 構造的型付け(structural typing)により、“形(プロパティの集合)が同じなら互換”。
- エディタ補完・リファクタリング・API 変更検知など、実務の体験が劇的に良くなる。
1) 歴史的経緯:TypeScript はなぜ生まれた?
2010年代、Webアプリが巨大化し、**型のない JS では“実行してみるまで壊れているか分からない”という悩みが増えました。
Microsoft は 2012 年に TypeScript を公開。“アプリケーション規模の JavaScript 開発”を掲げ、静的型付けと強力な言語サービス(補完・ジャンプ・安全なリネーム)**を提供しました。以後も継続的に機能強化されています。
🔎用語メモ:静的型付け … 実行前(コンパイル時)に型の不整合を検出する仕組み。
2) JavaScript と TypeScript の根本的な違い
2-1. 「JS の上位互換」+ 型は実行時に消える(型消去)
TS は JS の上位互換(superset)。JS で書けるものは TS でも有効です。
TS の型注釈はコンパイル時のみ使われ、生成されるのは 普通の .js。実行時に型は存在しません。
// TypeScript
function add(a: number, b: number): number {
return a + b;
}
// ↓コンパイル後(JavaScript): 型は消える
function add(a, b) { return a + b; }
2-2. **構造的型付け(structural typing)**が JS と相性抜群
TS の互換性は “形(プロパティの集合)で決まる” 方式。JS はオブジェクトの“形”を柔軟に扱う文化なので、実務で使いやすいです。
interface Pet { name: string }
class Dog { name = "Pochi" }
let p: Pet = new Dog(); // 形が同じなのでOK
2-3. 段階導入:既存 JS にも型チェックをかけられる
.js
にも JSDoc + // @ts-check で型チェック可能。少しずつ導入できます。
// @ts-check
/** @param {number} n */
function double(n) { return n * 2 } // 型エラーは編集時に通知
2-4. private と #private の違い(混同注意)
-
private
(TSの修飾子) … コンパイル時チェックのみ。 -
#private
(ECMAScript私有フィールド) … 実行時に本当に隠蔽。
3) TypeScript の“実務で効く”機能
3-1. 型注釈 & 型推論
注釈は 必要最小限でOK。TS はかなり賢く推論します。
const n = 42; // number と推論
const hello = "hello"; // "hello"(リテラル)または string と推論(文脈依存)
3-2. ユニオン / 交差型、ジェネリクス、interface
/ type
type Result = { ok: true; value: string } | { ok: false; error: Error };
function unwrap(r: Result): string {
return r.ok ? r.value : r.error.message;
}
3-3. 制御フロー解析(narrowing)
条件分岐にもとづいて 変数の型を自動で絞り込み ます。
function lengthOf(x: string | string[]) {
return typeof x === "string" ? x.length : x.length;
}
3-4. ユーティリティ型(Partial
/ Pick
/ Omit
/ Record
など)
既存型を 変形 するツール群。フォーム、API I/F などで大活躍します。
interface User { id: number; name: string; email: string }
type UserUpdate = Partial<User>; // すべて任意プロパティに
type UserPreview = Pick<User, "name"|"email"> // 一部だけ取り出す
3-5. テンプレートリテラル型
文字列を型レベルで組み立て、キーのパターンを厳密に表現できます。
type Event = "user" | "order";
type Topic = `${Event}.created` | `${Event}.updated`; // "user.created" など
3-6. as const
と const
型パラメータ
-
as const
… リテラルを きっちり固定。 -
const
型パラメータ … 関数側で 引数のリテラル性を保持。
const palette = { primary: "blue", danger: "red" } as const;
// type: { readonly primary: "blue"; readonly danger: "red" }
function tuple<const T extends readonly unknown[]>(x: T) {
return x; // T をそのまま保つ
}
3-7. satisfies
演算子
「このオブジェクトが型を満たすか」だけ検証し、推論は壊さない のがポイント。
type RGB = [number, number, number];
type Palette = Record<"red" | "green" | "blue", string | RGB>;
const p = {
red: [255, 0, 0],
green: "#0f0",
blue: [0, 0, 255]
} satisfies Palette;
// p.red は [number, number, number] と推論される
3-8. デコレータ(標準仕様に追随)
クラスやメンバーに再利用可能な付加機能を付ける構文。ツールチェーンの対応状況も確認しつつ活用。
// 例(概念イメージ)
function log(target: any, key: string) { /* ... */ }
class Service {
@log
run() {}
}
3-9. 宣言ファイル(.d.ts)と型定義(@types)
JS ライブラリに 型 を付ける仕組み。npm i -D @types/ライブラリ名
で入手可能。
npm i -D @types/lodash
3-10. 巨大コードベース向け:Project References / 増分ビルド
プロジェクトを分割し、型チェックと再ビルドを高速化。
4) Java など **名目型(Nominal Typing)**との違い
4-1. ざっくり対比
- TypeScript:構造的型付け … 形(メンバー構成)が一致すれば代入OK
-
Java:名目型付け … 宣言上の関係(
implements
/extends
)がないと代入NG
構造的(TS)
interface Pet { name: string }
class Dog { name = "Pochi" }
let p: Pet = new Dog(); // OK
名目型(Java)
interface Pet { String name(); }
class Dog { String name() { return "Pochi"; } }
// Pet p = new Dog(); // NG:Dog が Pet を implements していない
4-2. それぞれの実務的メリット/デメリット
観点 | TS(構造的) | Java(名目型) |
---|---|---|
柔軟性 | “形が同じ”ならすぐ使える | 関係を明示しないと使えない |
API適合 | オブジェクトリテラルでサクッと適合 | 型・継承関係を設計時に確定 |
安全性 | うっかり互換が起こり得る | 意図しない互換が起きにくい |
読みやすさ | “形”の把握が大変なことも | 宣言に関係が現れ追いやすい |
4-3. TS で“名目っぽく”するテク(ブランディング)
同じ number
でも USD と JPY を取り違えたくない … そんな時に ユニークシンボルでタグ付け します。
declare const USDBrand: unique symbol;
type USD = number & { readonly [USDBrand]: "USD" };
declare const JPYBrand: unique symbol;
type JPY = number & { readonly [JPYBrand]: "JPY" };
function pay(price: USD) {}
const priceUsd = 100 as USD;
const priceJpy = 100 as JPY;
pay(priceUsd); // OK
// pay(priceJpy); // コンパイルエラー:通貨の取り違えを防ぐ
5) 最初の一歩:導入レシピ(実務向け)
-
小さく試す:既存
.js
に JSDoc +// @ts-check
。よければ.ts
へ移行。 -
tsconfig
はstrict: true
で:厳しめに始め、必要に応じて例外を明示(// @ts-expect-error
)。 -
“必要十分な型” を心がける:過剰な注釈は書かず、推論・ユーティリティ型・
satisfies
を活用。 -
外部ライブラリは
@types/*
を確認:なければ最小の型宣言を足す。 - 大きくなったら分割:Project References + 増分ビルドで体感速度アップ。
6) まとめ
- ランタイム前にミスを潰せる/安全にリファクタできる。
- JS の柔軟さを保ちつつ、構造的型付けでちょうど良い安全性。
- 宣言ファイル・
@types/*
・エディタ連携など エコシステムが充実、段階導入もしやすい。
付録:用語ミニ辞典
- 構造的型付け … **形(プロパティ/メソッドの集合)**が合えば同じ型とみなす(TS)。
- 名目型付け … 名前・宣言で互換性を決める(Java/C#/…)。
- 型消去 … 出力 JS から型情報が消える(TS の型は実行時に存在しない)。
- narrowing(絞り込み) … 分岐などの 制御フロー解析で変数の型をより具体化。
-
ユーティリティ型 …
Partial
/Pick
など 型操作の内蔵ツール。 - テンプレートリテラル型 … 文字列を 型レベルで合成 して表現力を高める。