TypeScript 入門
TypeScript は、マイクロソフトが開発しオープンソース化したプログラミング言語です。JavaScript のスーパーセットであり、JavaScript の構文と完全に互換性を持ちながら、オプションの静的型システムとクラスベースのオブジェクト指向プログラミング機能を追加しています。
TypeScript のコードは最終的に純粋な JavaScript にコンパイルされ、ブラウザ、Node.js、モバイルなど、JavaScript をサポートするあらゆる環境で実行できます。

なぜ TypeScript が必要か
JavaScript は動的型付け言語であり、変数の型は実行時にしか確定しません。
この柔軟性は小規模なプロジェクトでは優れていますが、プロジェクトの規模が大きくなるにつれて、問題が明らかになってきます。
| 問題となるシナリオ | JavaScript の課題 | TypeScript の解決方法 |
|---|---|---|
| 関数の引数に間違った型を渡す | 実行時になって初めてエラーが発生し、事前に発見するのが難しい | コンパイル段階でエラーになり、IDE がリアルタイムに警告する |
| 存在しないプロパティにアクセスする | 返すundefined、動作は予測しにくい | コンパイラが直接エラーを報告し、通過を拒否する |
| 大規模プロジェクトのリファクタリング | 一箇所を変更すると、どこが壊れるか分からない | 型システムがすべての参照を自動的に追跡する |
| チームでの協力 | 関数がどんな引数を受け取り、何を返すかはすべてコメントやドキュメントに依存する | 型シグネチャがドキュメントとなり、IDE が自動補完する |
| コードの可読性 | 関数定義を見てもデータ構造を直接知ることはできない | インターフェースと型エイリアスにより、データ構造が一目瞭然になる |
TypeScript の本質的な目標は、JavaScript を置き換えることではなく、大規模な JavaScript プロジェクトを管理可能で保守可能にすることです。TypeScript は JavaScript の「安全網」であり、競争相手ではありません。
もっともわかりやすい例を見てみましょう:
例
function greet(name) {
return "Hello, " + name.toUpperCase();
}
greet(123); // 実行時エラー:name.toUpperCase is not a function
例
function greet(name: string): string {
return "Hello, " + name.toUpperCase();
}
greet(123); // コンパイルエラー:Argument of type 'number' is not assignable to parameter of type 'string'
greet("サンプル"); // 正しい:Hello, サンプル
TypeScript と JavaScript の関係
TypeScript と JavaScript の関係は、一言でまとめられます:TypeScript は JavaScript のスーパーセットです、つまり、すべての有効な JavaScript コードは、同時に有効な TypeScript コードでもあります。
集合関係の図
JavaScript(すべての JS コード)
⊂ TypeScript(JS + 型システム + 新機能)
⊂ コンパイル成果物(標準 JavaScript、あらゆる環境で実行可能)
両者の核心的な違いの比較:
| 比較項目 | JavaScript | TypeScript |
|---|---|---|
| 型システム | 動的型付け、実行時に決定 | 静的型付け、コンパイル時にチェック(オプション) |
| 実行方法 | ブラウザ / Node.js で直接実行 | JS にコンパイルしてから実行する必要がある |
| エラー発見のタイミング | 実行時 | コンパイル時(事前に発見) |
| IDE サポート | 基本的な補完 | 強力な型推論、正確な補完、リファクタリングサポート |
| 学習曲線 | 比較的なだらか | 型システムを追加で学ぶ必要がある |
| 既存 JS との互換性 | — | 完全互換、段階的に移行可能 |
| ファイル拡張子 | .js | .ts 或 .tsx(JSX を含む) |
TypeScript は「段階的採用」をサポートしています。プロジェクト全体を一度に書き直す必要はありません。まず
.jsに変更し.ts、段階的に重要なモジュールに型を追加し、既存のコードはそのまま動作します。
コア機能
TypeScript は JavaScript を基盤として、完全な型システムを導入しています。以下は最もよく使われるコア機能です。
基本的な型注釈
変数、関数の引数、戻り値の後にコロンで型を宣言します。これは TypeScript の最も基本的な書き方です。
例
let age: number = 25;
let username: string = "サンプル";
let isActive: boolean = true;
// 配列型、2つの等価な書き方
let scores: number[] = [90, 85, 92];
let tags: Array<string> = ["typescript", "javascript"];
// タプル:固定長と固定型の配列
let point: [number, number] = [10, 20];
let entry: [string, number] = ["サンプル", 100];
// 関数:引数の型 + 戻り値の型
function add(a: number, b: number): number {
return a + b;
}
// void:関数に戻り値がない
function log(msg: string): void {
console.log(msg);
}
// オプション引数:引数名の後に ? を付ける
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
console.log(greet("サンプル")); // 出力:サンプル
console.log(greet("サンプル", "Mr.")); // 出力:Mr. サンプル
インターフェース(Interface)
インターフェースはオブジェクトの「形状」(Shape)を記述し、オブジェクトが持つべきプロパティとメソッドを定義します。
例
interface User {
id: number; // 必須
name: string; // 必須
email?: string; // オプション、? でマーク
readonly role: string; // 読み取り専用、代入後に変更不可
}
// 関数の引数にインターフェース型を使用
function printUser(user: User): void {
console.log(`ID: ${user.id}, Name: ${user.name}`);
if (user.email) {
console.log(`Email: ${user.email}`);
}
}
const admin: User = {
id: 1,
name: "サンプル",
email: "サンプル@example.com",
role: "admin",
};
printUser(admin);
// 出力:ID: 1, Name: サンプル
// 出力:Email: サンプル@example.com
// admin.role = "user"; // エラー:Cannot assign to 'role' because it is a read-only property.
// インターフェースの継承
interface AdminUser extends User {
permissions: string[];
}
型エイリアス(Type Alias)
型エイリアスはtypeキーワードで作成し、ユニオン型や交差型などの複雑な構造を含む、あらゆる型にエイリアスを定義できます。
例
type ID = string | number;
let userId: ID = "abc-123"; // 合法
userId = 456; // これも合法
// リテラル型:変数は特定の値のみに制限
type Direction = "up" | "down" | "left" | "right";
type Status = "pending" | "active" | "inactive";
function move(dir: Direction): void {
console.log(`Moving ${dir}`);
}
move("up"); // 正しい
// move("diagonal"); // エラー:リテラルの範囲外
// 交差型:複数の型を結合
type WithTimestamp = {
createdAt: Date;
updatedAt: Date;
};
type UserRecord = User & WithTimestamp; // User と WithTimestamp のすべてのプロパティを同時に持つ
// 関数型エイリアス
type Transformer<T, U> = (input: T) => U;
const toNumber: Transformer<string, number> = (s) => parseInt(s, 10);
ジェネリック(Generics)
ジェネリックは再利用可能なコンポーネントを書きつつ、型安全性を保つことを可能にします。ジェネリックは「型の変数」と理解できます——使用時に具体的な型を渡します。
例
function firstNumber(arr: number[]): number {
return arr[0];
}
// ジェネリックを使用:T は型パラメータで、呼び出し時に具体的な型が決まる
function first<T>(arr: T[]): T {
return arr[0];
}
const n = first<number>([1, 2, 3]); // n の型は number
const s = first<string>(["a", "b"]); // s の型は string
const inferred = first([true, false]); // TypeScript は T を boolean と自動推論する
// ジェネリックインターフェース
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
// ジェネリックインターフェースの使用
const userResponse: ApiResponse<User> = {
data: { id: 1, name: "サンプル", role: "admin" },
status: 200,
message: "success",
};
// ジェネリック制約:T に id プロパティが必須であることを制限する
function getById<T extends { id: number }>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id);
}
列挙型(Enum)
列挙型は一連の名前付き定数を定義するために使われ、コード内の「マジックナンバー」や「マジック文字列」を意味のあるものにします。
例
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
console.log(Direction.Up); // 出力:0
console.log(Direction[0]); // 出力:"Up"(逆方向のマッピング)
// 文字列列挙(より推奨。デバッグ時に値が読みやすい)
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
function paint(color: Color): void {
console.log(`Painting in ${color}`);
}
paint(Color.Red); // 出力:Painting in RED
// paint("red"); // エラー:文字列 "red" は Color 型ではない
// const 列挙:コンパイル後にインライン化され、実行時オーバーヘッドを削減
const enum HttpStatus {
OK = 200,
NotFound = 404,
InternalError = 500,
}
const status: HttpStatus = HttpStatus.OK; // コンパイル後に直接数値 200 に置き換えられる
型推論
TypeScript はすべての変数に手動で型を注釈する必要はなく、コンパイラが代入に基づいて自動的に推論します。
例
let count = 0; // number と推論
let name = "サンプル"; // string と推論
let flag = true; // boolean と推論
// 配列の要素の型を推論
let numbers = [1, 2, 3]; // number[] と推論
// 関数の戻り値の型を推論
function double(n: number) { // 戻り値は自動的に number と推論
return n * 2;
}
// オブジェクトリテラルの推論
const config = {
host: "localhost", // string
port: 3000, // number
debug: false, // boolean
};
// config.port = "3000"; // エラー:Type 'string' is not assignable to type 'number'.
実用的なアドバイス:関数のパラメータには通常、明示的な型注釈が必要です(呼び出し側はパラメータの値から型を推論できないため)。一方、関数内のローカル変数や戻り値は、ほとんどの場合推論に任せてよく、すべて手動で注釈する必要はありません。
クラスとアクセス修飾子
TypeScript は ES6 のクラス構文を完全にサポートし、さらにpublic、private、protected、readonly4つのアクセス修飾子が追加されました。
例
readonly name: string; // 読み取り専用。初期化後は変更不可
private age: number; // プライベート。クラス内部からのみアクセス可能
protected species: string; // プロテクテッド。サブクラスからアクセス可能
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
// public メソッド(デフォルトで public のため省略可)
public introduce(): string {
return `I'm ${this.name}, a ${this.species}.`;
}
// getter:プロパティにアクセスするようにメソッドを呼び出す
get info(): string {
return `${this.name} (${this.age} years old)`;
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age, "Canis lupus familiaris"); // 親クラスのコンストラクタを呼び出す
this.breed = breed;
}
// サブクラスは protected プロパティにアクセスできる
describe(): string {
return `${this.name} is a ${this.species}, breed: ${this.breed}`;
}
}
const dog = new Dog("サンプル", 3, "Labrador");
console.log(dog.introduce()); // 出力:I'm サンプル, a Canis lupus familiaris.
console.log(dog.info); // 出力:サンプル (3 years old)
// console.log(dog.age); // エラー:プライベートプロパティは外部からアクセスできない
デコレータ(Decorator)
デコレータはメタプログラミング構文であり、クラス・メソッド・プロパティに追加の振る舞いを付与するために使われます。Angular や NestJS などのフレームワークでよく見られます。
TypeScript 5.0 ではデコレータが正式標準(Stage 3 提案)として実装され、もはやexperimentalDecoratorsフラグを有効にする必要はありません。
例
function sealed(constructor: Function) {
Object.seal(constructor); // 新しいプロパティの追加を禁止
Object.seal(constructor.prototype);
}
// メソッドデコレータ:ログ記録や権限チェックなどに利用可能
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = original.apply(this, args);
console.log(`${propertyKey} returned:`, result);
return result;
};
}
@sealed
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(1, 2);
// 出力:Calling add with args: [ 1, 2 ]
// 出力:add returned: 3
応用分野
TypeScript は JavaScript との完全な互換性により、JavaScript が動作するほぼすべての場所で使用でき、大規模プロジェクトでは追加の利点をもたらします。
フロントエンド Web 開発
これは TypeScript が最も広く使われるシーンです。3大主要フロントエンドフレームワークはすべて TypeScript をファーストクラスでサポートしています:
| フレームワーク | TypeScript サポート状況 | 代表的な用途 |
|---|---|---|
| Angular | 公式言語であり、デフォルトで TypeScript を使用、回避不可 | エンタープライズ向け SPA、管理画面システム |
| React | 公式が提供@types/react、Create React App と Vite はいずれも TypeScript テンプレートを内蔵 | EC、コンテンツプラットフォーム、中管理画面向けアプリケーション |
| Vue | Vue 3 のコアコードは TypeScript で書き直され、Composition API の TypeScript 親和性が大幅に向上 | 中小規模プロジェクト、段階的移行 |
| Next.js / Nuxt | TypeScript サポートを内蔵、新規プロジェクトではデフォルトで有効 | フルスタック SSR/SSG アプリケーション |
バックエンド Node.js 開発
TypeScript は Node.js バックエンド開発でも同様に普及しています。NestJSは現在最も人気のある TypeScript バックエンドフレームワークであり、Angular に近いモジュール式アーキテクチャを採用し、依存性注入とデコレータサポートを内蔵しています。
さらに、Denoランタイムは初日から TypeScript をネイティブサポートしており、設定なしで直接実行できます。.tsファイル。Bun同様に TypeScript サポートを内蔵しています。
コマンドラインツールとスクリプト
を活用することでts-node、tsxなどのツールにより、TypeScript コードは事前コンパイルなしで直接実行でき、ビルドスクリプトや CLI ツールの作成に非常に適しています。
モバイルとクロスプラットフォーム
React NativeTypeScript を完全サポートしており、多くのエンタープライズ向けモバイルアプリ(Microsoft Office モバイル版、Shopify など)がこの技術スタックを採用しています。Expoの新規プロジェクトテンプレートもデフォルトで TypeScript をベースとしています。
ゲーム開発とグラフィックス
Babylon.js(マイクロソフト製の 3D エンジン)は完全に TypeScript で書かれており、TypeScript の型定義をファーストクラスとして扱っています。Phaserなどの 2D ゲームエンジンも完全な型定義を提供しています。
発展の歴史
TypeScript の誕生と成長の歴史は JavaScript エコシステムの進化と密接に関連しており、主要バージョンのリリースにはそれぞれ明確なエンジニアリングニーズが背景にあります。
起源(2010〜2012年)
TypeScript の物語は、マイクロソフト内部のエンジニアリング上の課題、すなわち「大規模な JavaScript プロジェクトを保守可能にするにはどうすればよいか」から始まりました。
設計者Anders Hejlsbergは業界の伝説的存在——C# のチーフアーキテクトであり、Turbo Pascal と Delphi の生みの親です。彼は 2010 年頃にこの言語の設計を開始し、中核目標は JavaScript との互換性を壊さずに静的型チェックを導入することでした。
マイクロソフトが TypeScript を発表したもう一つの背景は Windows 8 のリリースです——アプリケーションを HTML + JavaScript で開発できるようになり、マイクロソフトは .NET プログラマーの参加を引き付けたいと考えました。そのため TypeScript の多くの構文は意図的に C# や .NET に近づけられています。
2012 年 10 月、TypeScript 0.8 が公開リリースされました。それ以前にマイクロソフト内部で約 2 年間開発されていました。
初期バージョン(2013〜2015年)
| 時期 | バージョン | 重要な出来事 |
|---|---|---|
| 2013-06 | 0.9 | 正式な安定版がリリースされ、パフォーマンスが大幅に向上、IDE 統合がより充実 |
| 2014-04 | 1.0 | 最初の正式安定版。クラス・インターフェース・モジュールを導入し、標準の JavaScript へのコンパイルをサポート |
| 2014-07 | — | TypeScript コンパイラのコードがオープンソース化され、GitHub でホストされ、コミュニティからの貢献を受け付け |
| 2015-04 | — | マイクロソフトが Visual Studio Code をリリースし、TypeScript への深いサポートを内蔵、両者は相互に成長 |
| 2015-07 | 1.5 | ES6 モジュール構文のサポート、デコレータ(実験的)、名前空間を導入 |
急速な成長(2016〜2019年)
この段階は TypeScript がニッチなツールから主流へと移行する重要な時期であり、中核となる推進力はフレームワークエコシステムの選択にありました。
| 時期 | バージョン / 出来事 | マイルストーン的意義 |
|---|---|---|
| 2016-09 | TypeScript 2.0 | 非Null型の導入(--strictNullChecks)、これはTypeScriptの型安全性において最も重要な特徴の一つ。タグ付きユニオン型の導入 |
| 2016 | Angular 2 リリース | GoogleのAngularチームがTypeScriptを公式開発言語として採用することを発表し、TypeScriptが広く認知されるきっかけとなった |
| 2017 | TypeScript 2.x シリーズ | 条件型、マップ型、inferキーワードが相次いで導入され、型システムの表現力が大幅に向上 |
| 2018-07 | TypeScript 3.0 | プロジェクト参照(Project References)を導入し、大規模なmonorepoをサポート。unknown型(anyよりも安全なトップ型)を導入 |
| 2019 | DefinitelyTyped | コミュニティ管理の型定義リポジトリDefinitelyTyped(@types/*)は7000以上のパッケージを超え、主要なnpmライブラリをほぼすべてカバー |
成熟と普及(2020〜2022年)
| 時期 | バージョン / イベント | 重要な内容 |
|---|---|---|
| 2020-08 | TypeScript 4.0 | 可変タプル型、ラベル付きタプル要素。エディタの体験が大幅に改善 |
| 2021 | TypeScript 4.x シリーズ | テンプレートリテラル型(Template Literal Types)、組み込みユーティリティ型の強化、noImplicitOverrideなどのstrictモードの強化 |
| 2022 | State of JS 調査 | TypeScriptの使用率が初めて純粋なJavaScriptを上回り、JSエコシステムで最も人気のあるスーパーセット言語となった |
| 2022 | Vue 3 / Vite の普及 | Vue 3のコアがTypeScriptで書き直され、Viteと組み合わせることでフロントエンド開発のTypeScript体験が新たな高みに達した |
現代の段階(2023年〜現在)
| 時期 | バージョン | 重要な特徴 |
|---|---|---|
| 2023-03 | TypeScript 5.0 | モダンデコレータ(Stage 3標準)、const型パラメータ、すべての列挙型がユニオン列挙型にアップグレードされ、パフォーマンスが大幅に向上 |
| 2023-08 | TypeScript 5.2 | Explicit Resource Managementの導入(usingキーワード)。データベース接続やファイルハンドルなどのリソースの決定的なクリーンアップ問題を解決 |
| 2024-03 | TypeScript 5.4 | クロージャ内の型絞り込みの改善、NoInferユーティリティ型 |
| 2024-06 | TypeScript 5.5 | 型述語の推論、正規表現の構文チェックにより、型システムがコードロジックをより正確に理解できる |
| 2024-11 | TypeScript 5.7 | サポート--target es2024、未初期化変数の検出がより信頼できる |
| 2025-03 | TypeScript 5.8 | 条件型とインデックスアクセス型の戻り値チェックを強化。以下をサポート:--module nodenext環境下で〜を介してrequire()ESMモジュールのロード(Node.js 22+が必要) |
将来の展望:Go でコンパイラを書き直す
2025年初頭、マイクロソフトは重大な決定を発表:TypeScriptコンパイラをGo言語で書き直す。プロジェクトのコードネームはCorsa。
公式によると、書き換え後のコンパイラはパフォーマンスが10倍以上向上する見込みで、大規模プロジェクトのビルド時間は分単位から秒単位に短縮される。
計画によると、Go版はTypeScript 7.xで正式リリースされる予定で、現在のTypeScript 5.x / 6.xは引き続き通常どおり反復開発とメンテナンスが行われる。リファクタリングは2025年末に完了する見込み。
今回の書き換えでTypeScript言語自体の構文や型システムが変わることはなく、開発者にとっては透過的なアップグレードとなる。コードを変更する必要は一切なく、コンパイル速度が大幅に向上するだけだ。
TypeScript と他の静的型付け言語の比較
他の強い型付け言語のバックグラウンドがあれば、以下の比較によってTypeScriptの設計思想を素早く把握できるだろう。
| 比較項目 | TypeScript | Java / C# | Go |
|---|---|---|---|
| 型システム | 構造的型付け(ダックタイピング) | 名前的型付け(継承関係を明示的に宣言する必要がある) | 構造的型付け(暗黙のインターフェース) |
| ヌル安全性 | 有効にするとstrictNullChecksサポートされる | JavaはアノテーションやOptionalを利用する必要があり、C# 8+はサポート | error値とnilチェックによる |
| ジェネリクス | サポート。構文はJava/C#に近い | サポート。Javaには型消去の制限がある | Go 1.18+でサポート。構文は比較的シンプル |
| コンパイル成果物 | JavaScript(JS環境で実行) | バイトコード(JVM / CLR上で実行) | ネイティブマシンコード |
| 実行時型チェック | なし(型情報はコンパイル後に消去される) | あり(リフレクション機構) | 无 |
| 学習曲線 | JS開発者に優しく、段階的に採用可能 | 初心者には比較的急 | 比較的シンプルで、学習曲線は中程度 |
TypeScriptが使用しているのは構造的型システム(Structural Typing):2つの型の「形状」が一致していれば互換性があると見なされ、継承関係を明示的に宣言する必要はない。これはJava/C#の名前の型付け(Nominal Typing)とは本質的に異なり、TypeScriptがJavaScriptとシームレスに相互運用できる鍵となる理由でもある。
TypeScript の限界
TypeScriptは銀の弾丸ではない。その境界を理解することも同様に重要だ。
| 限界 | 説明 | 対応策 |
|---|---|---|
| 実行時に型がない | 型情報はコンパイル後にすべて消去され、実行時に型に依存した判断はできない | 実行時検証が必要な場合は使用するzod、io-tsなどのライブラリ |
| コンパイル工程 | 純粋なJSと比べてコンパイル工程が1つ増え、プロジェクトの複雑さが増す | 現代のビルドツール(Vite、esbuild)はこれに対してすでに良好な最適化が行われている |
| any型の脱出ハッチ | 乱用するとany型チェックが形骸化してしまう | 有効化noImplicitAnyとstrictモード、ESLintルールとの併用 |
| 型体操のハードル | 複雑な条件型やマップ型は初心者に不親切 | ほとんどのビジネスコードは高度な型を必要としないため、段階的に学べばよい |
| サードパーティライブラリのサポート | 一部の古いライブラリには型定義がなく、自分で.d.tsを作成する必要がある | まず確認する@types/*、それでもなければ自分で宣言ファイルを書く |