TypeScript

JavaScript に型を書く手段を足した言語。構造的型付け、型が実行時に残らない仕組み、消せない構文と型定義ファイルの扱いを解説

言語フロントエンド
「TypeScript」の技術書を見る →

TypeScript とは

TypeScript は、Microsoft が 2012 年に公開した言語で、JavaScript の構文に型を書く手段を足したものである。宣言した型は翻訳時に照合され、不整合はその場でエラーになる。ただし型は出力される JavaScript から取り除かれるため、実行時の土台は JavaScript のままである。

版の更新は速い。npm で配布される latest は 2026 年 8 月時点で 7.0.2 (2026 年 7 月公開) で、5 系の最後は 5.9.3 (2025 年 9 月公開) である。設定項目や診断メッセージは版ごとに増えるので、手元のコンパイラーの版を確かめてから資料を読むほうが迷わない。

JavaScript との違いが出る場所

差が出るのは書くときと翻訳するときで、実行時の土台は共通である。「TypeScript なら実行時エラーが出ない」と読むのは誤りで、翻訳時に止められるのは宣言した型の範囲の不整合だけである。外から届いた値の形が想定と違えば、実行時に落ちるのは JavaScript と変わらない。

観点JavaScriptTypeScript
型の在りか値が実行時に型を持つ宣言を翻訳時に照合し、出力からは消える
型の不整合実行して初めて分かる宣言した範囲は翻訳時に分かる
実行時のエラー起きる同じように起きる
編集支援値からの推測に頼る宣言を根拠に補完 / 参照検索 / 名前変更が効く
導入の手間実行環境があれば動く翻訳の段取りと型定義の用意が必要

型の基本

書き方は変数や引数の後ろにコロンで型を添える形が中心で、型自体にも名前を付けて使い回せる。よく使うのは次の 4 系統である。

// プリミティブ型
const name: string = 'Alice';
const age: number = 30;
const active: boolean = true;

// オブジェクト型
type User = { name: string; age: number; email?: string };

// ユニオン型
type Status = 'active' | 'inactive' | 'suspended';

// ジェネリクス
function first<T>(items: T[]): T | undefined { return items[0]; }

型の推論

初期値から型が決まる場所では注釈を省ける。書きすぎると、実装を変えたときに注釈だけが古い前提で残る。明示する価値が高いのは、関数の引数と戻り値のように呼び出し側との約束になる場所である。

// TypeScript は型を推論する (明示的な型注釈は不要な場合が多い)
const items = [1, 2, 3]; // number[] と推論
const user = { name: 'Alice', age: 30 }; // { name: string; age: number } と推論

判別可能なユニオン型

成否のようにどちらか一方の形しか取らないデータは、判別用のフィールドを共通に持つユニオン型で表す。すると分岐した先で参照できるフィールドが自動的に絞られ、成功時にしか存在しない値を失敗の枝で読む誤りが翻訳時に止まる。

type Result<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };

function handle(result: Result<number>) {
  if (result.ok) {
    console.log(result.value); // TypeScript が value の存在を保証
  } else {
    console.error(result.error);
  }
}

tsconfig.json

翻訳の挙動はほぼこの設定ファイルで決まる。既存の JavaScript を移行する途中か、新規に始めるかで妥当な値は変わる。次は新規に始める場合の出発点である。

{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "noUncheckedIndexedAccess": true
  }
}

strict: true は最初に入れる価値が高い。null や undefined になり得る経路と、暗黙の any が型に現れるようになるためである。一方、インデックス参照の結果に undefined を足す noUncheckedIndexedAccess は strict に含まれない独立した設定で、手元の 6.0.3 でも strict だけでは範囲外アクセスがエラーにならないことを確かめた (2026 年 8 月時点)。既存コードへ後から入れるなら、対象を絞った設定を別に用意して範囲を広げるほうが完走しやすい。

型の絞り込みと網羅性

型ガードで if (result.ok) のように条件分岐すると、その枝の中で型が自動的に絞り込まれる。type Route = `/api/${string}` のようなテンプレートリテラル型や T extends string ? A : B のような条件型を組み合わせれば、文字列の形や型による分岐まで型で表せる。

網羅性の検査は、switch を書けば自動で効くわけではない。ユニオン型の候補が増えたときに未処理の枝を見つけたいなら、戻り値の型を明示して全経路が値を返すことを要求するか、default の枝で判別値を never 型の変数へ代入する。手元の 6.0.3 で確かめると、前者は関数に終端の return がないという指摘になり、後者は未処理の候補が never に代入できないという指摘になった。どちらも書かない場合は、switch を抜けて undefined が返るだけで指摘は出ない (2026 年 8 月時点の実測)。

構造的型付け

TypeScript は名前ではなく形で型を照合する。同じ形の型を別の名前で 2 つ宣言しても、両者は互換である。クラスも原則としてメンバーの形で判定されるので、継承関係のない 2 つのクラスが同じ型として通る。例外は private や protected なメンバーを持つ場合で、このときは宣言元が同じかどうかまで見られ、形が同じでも代入は通らない。

もう 1 つ厳しくなるのが、オブジェクトリテラルを直接代入する場面である。type P = { x: number } に対して { x: 1, y: 2 } をその場で書くと、y は P に存在しないという指摘 (TS2353) を受ける。同じ値をいったん変数に入れてから代入すれば通る。綴り間違いを拾うための追加の検査であって、互換性の判定が名前を見るようになったわけではない。

型は実行時に残らない

出力される JavaScript には型注釈が残らない。公式の設計方針でも、実行時の型情報をプログラムに持ち込むことと、型の判定結果によって別のコードを出力することは非目標として明示されている。つまり型は実行時の検証を代替しない。通信の応答や設定ファイルの読み込みのように外から来る値は、実行時に形を確かめる処理を自分で書く必要がある。型を落として JavaScript を得る工程そのものは トランスパイラ 側の話題である。

型を消すだけでは動かない構文

型注釈は消せば済むが、enum、実装を持つ namespace、コンストラクタ引数の修飾子による自動代入は、実行時のコードを新たに生む構文である。この差は、実行環境へ TypeScript を直接渡すときに表面化する。Node.js v26 では型注釈だけのファイルはそのまま実行できたのに対し、enum を含むファイルは対応外の構文として実行が止まった。この種の構文を使っていないことを翻訳側で保証したければ erasableSyntaxOnly を有効にする。手元の 6.0.3 では enum に対して TS1294 が出た (いずれも 2026 年 8 月時点の実測)。代替はユニオン型か、as const を付けたオブジェクトである。

型定義ファイルと DefinitelyTyped

.d.ts は実装を持たず型だけを宣言するファイルで、JavaScript で書かれたライブラリに後から型を与える手段になる。型定義を同梱していないライブラリの型は DefinitelyTyped という共同管理のリポジトリに集められ、@types/ を接頭辞に持つパッケージとして配布される。Node.js の標準 API に対する @types/node がその代表である。型定義は実装とは別に更新されるため、版がずれると実装にある関数が型に無い、あるいはその逆の状態が起きる。噛み合わないときは実装とその型定義の両方の版を疑う。

導入をどう判断するか

見るべき点は 3 つある。第 1 に、寿命と参加人数である。長く保守し、書いた人以外が触るコードほど、宣言が読み手への説明として効く。第 2 に、境界の多さである。通信 / 保存 / 設定のように外と値をやり取りする箇所が多いほど、想定する形を書いておく利点が大きい。第 3 に、翻訳の段取りを維持できるかである。設定と型定義の更新を続ける手間を払えない環境では、型の恩恵より腐った型定義の害が勝つ。

共有:

この記事は役に立ちましたか?

関連用語

関連する記事