TypeScriptは、JavaScriptに「型」の概念を追加した言語であり、大規模なWebアプリケーション開発のデファクトスタンダードとして定着しています。2026年現在、React、Next.js、Vue.js、Angular、Node.jsなど、主要なフレームワークやランタイムのほとんどがTypeScriptを標準でサポートしています。
本記事では、TypeScriptをこれから学ぶ方に向けて、JavaScriptとの違い、環境構築、基本的な型システム、実践的なコード例までを解説します。JavaScript経験者はもちろん、プログラミング初心者の方にも理解できる内容を目指しています。
TypeScriptとは?JavaScriptとの違い
TypeScriptは、Microsoftが開発したオープンソースのプログラミング言語で、JavaScriptの「スーパーセット」として位置づけられています。つまり、すべてのJavaScriptコードは有効なTypeScriptコードです。
TypeScriptが解決する課題
JavaScriptは柔軟で強力な言語ですが、動的型付けに起因する以下の課題があります。
// JavaScriptでよくあるバグ
function calculateTotal(price, quantity) {
return price * quantity;
}
// 文字列が渡されても実行時までエラーにならない
const total = calculateTotal("100", 3); // "100100100"(意図しない結果)
// TypeScriptなら型エラーで検出される
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
// コンパイル時にエラーとして検出される
// const total = calculateTotal("100", 3); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
JavaScriptとの主な違い
| 項目 | JavaScript | TypeScript |
|---|---|---|
| 型システム | 動的型付け | 静的型付け(型推論あり) |
| コンパイル | 不要(インタプリタ) | JavaScript へのトランスパイルが必要 |
| エラー検出 | 実行時 | コンパイル時 + 実行時 |
| IDE支援 | 限定的 | 強力な補完・リファクタリング |
| 学習コスト | 低い | やや高い(型の知識が必要) |
| ファイル拡張子 | .js | .ts / .tsx |
環境構築|TypeScriptを始める準備
Node.jsのインストール
TypeScriptの実行にはNode.jsが必要です。Node.js公式サイトからLTS版をインストールしてください。
# Node.jsのバージョン確認
node --version
npm --version
TypeScriptプロジェクトの作成
# プロジェクトディレクトリの作成
mkdir ts-tutorial
cd ts-tutorial
# package.jsonの初期化
npm init -y
# TypeScriptのインストール
npm install -D typescript
# tsconfig.jsonの生成
npx tsc --init
tsconfig.jsonの推奨設定
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
最初のTypeScriptファイル
src/index.tsを作成します。
const greeting = (name: string): string => {
return `こんにちは、${name}さん!`;
};
console.log(greeting("TypeScript"));
console.log(greeting("世界"));
# コンパイルと実行
npx tsc
node dist/index.js
# ts-nodeで直接実行(開発時)
npm install -D ts-node
npx ts-node src/index.ts
基本的な型システム
プリミティブ型
// 基本的な型
const name: string = "田中太郎";
const age: number = 30;
const isActive: boolean = true;
const value: null = null;
const data: undefined = undefined;
// 型推論:明示的に書かなくても型が推論される
const message = "Hello"; // string と推論される
const count = 42; // number と推論される
配列とタプル
// 配列
const numbers: number[] = [1, 2, 3, 4, 5];
const names: Array<string> = ["田中", "佐藤", "鈴木"];
// タプル(要素数と型が固定された配列)
const coordinate: [number, number] = [35.6762, 139.6503];
const entry: [string, number] = ["田中", 30];
// readonly 配列
const readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // Error: Property 'push' does not exist
オブジェクト型
// オブジェクト型の定義
const user: { name: string; age: number; email?: string } = {
name: "田中",
age: 30,
};
// type エイリアス
type User = {
id: number;
name: string;
email: string;
age?: number; // オプショナルプロパティ
};
const tanaka: User = {
id: 1,
name: "田中太郎",
email: "tanaka@example.com",
};
ユニオン型とリテラル型
// ユニオン型:複数の型のいずれか
type StringOrNumber = string | number;
function formatId(id: StringOrNumber): string {
if (typeof id === "string") {
return id.toUpperCase();
}
return `ID-${id.toString().padStart(5, "0")}`;
}
console.log(formatId("abc")); // "ABC"
console.log(formatId(42)); // "ID-00042"
// リテラル型:特定の値のみを許容
type Status = "active" | "inactive" | "pending";
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
function setStatus(status: Status): void {
console.log(`ステータスを ${status} に変更しました`);
}
setStatus("active"); // OK
// setStatus("unknown"); // Error
interfaceとtype|オブジェクトの型定義
interfaceの定義
interface User {
id: number;
name: string;
email: string;
age?: number;
readonly createdAt: Date;
}
// interfaceの拡張
interface Employee extends User {
department: string;
salary: number;
}
const employee: Employee = {
id: 1,
name: "田中太郎",
email: "tanaka@example.com",
department: "開発",
salary: 600,
createdAt: new Date(),
};
interfaceとtypeの使い分け
| 機能 | interface | type |
|---|---|---|
| オブジェクトの型定義 | 可 | 可 |
| 拡張(extends) | 可 | 交差型(&)で代替 |
| 宣言のマージ | 可(同名で自動マージ) | 不可 |
| ユニオン型の定義 | 不可 | 可 |
| タプル型の定義 | 不可 | 可 |
| マップ型の定義 | 不可 | 可 |
一般的なガイドラインとして、オブジェクトの形状を定義する場合はinterfaceを、ユニオン型やユーティリティ型を使う場合はtypeを選択します。
// interface が適しているケース
interface ApiResponse {
status: number;
data: unknown;
message: string;
}
// type が適しているケース
type Result<T> = { success: true; data: T } | { success: false; error: string };
type Nullable<T> = T | null;
type EventName = "click" | "hover" | "scroll";
関数の型定義
関数の基本的な型定義
// 関数宣言
function add(a: number, b: number): number {
return a + b;
}
// アロー関数
const multiply = (a: number, b: number): number => a * b;
// オプショナル引数
function greet(name: string, greeting?: string): string {
return `${greeting ?? "こんにちは"}、${name}さん!`;
}
// デフォルト引数
function createUser(name: string, role: string = "member"): User {
return { id: Date.now(), name, email: "", createdAt: new Date() };
}
// レストパラメータ
function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}
コールバック関数の型定義
// コールバックの型定義
type EventHandler = (event: { type: string; target: string }) => void;
function addEventListener(eventName: string, handler: EventHandler): void {
// イベントリスナーの登録処理
handler({ type: eventName, target: "button" });
}
addEventListener("click", (event) => {
console.log(`${event.type} on ${event.target}`);
});
// Promiseを返す非同期関数
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
型の絞り込み(Type Narrowing)
ユニオン型を使用する場合、特定の型に絞り込む処理が重要です。
// typeof による絞り込み
function printValue(value: string | number): void {
if (typeof value === "string") {
console.log(value.toUpperCase()); // string として扱える
} else {
console.log(value.toFixed(2)); // number として扱える
}
}
// in演算子による絞り込み
interface Dog {
type: "dog";
bark(): void;
}
interface Cat {
type: "cat";
meow(): void;
}
type Animal = Dog | Cat;
function handleAnimal(animal: Animal): void {
if (animal.type === "dog") {
animal.bark();
} else {
animal.meow();
}
}
// instanceof による絞り込み
function processError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
if (typeof error === "string") {
return error;
}
return "不明なエラー";
}
// カスタム型ガード
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value
);
}
function processData(data: unknown): void {
if (isUser(data)) {
console.log(data.name); // User 型として安全にアクセスできる
}
}
実践:型安全なAPIクライアントの作成
ここまでの知識を活用して、型安全なAPIクライアントを作成してみましょう。
// types.ts
interface ApiUser {
id: number;
name: string;
email: string;
role: "admin" | "member" | "guest";
}
interface ApiResponse<T> {
success: boolean;
data: T;
message?: string;
}
interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
perPage: number;
}
// api-client.ts
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
private async request<T>(
path: string,
options?: RequestInit
): Promise<ApiResponse<T>> {
const response = await fetch(`${this.baseUrl}${path}`, {
headers: { "Content-Type": "application/json" },
...options,
});
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
return response.json();
}
async getUser(id: number): Promise<ApiUser> {
const response = await this.request<ApiUser>(`/users/${id}`);
return response.data;
}
async getUsers(
page: number = 1,
perPage: number = 20
): Promise<PaginatedResponse<ApiUser>> {
const response = await this.request<PaginatedResponse<ApiUser>>(
`/users?page=${page}&perPage=${perPage}`
);
return response.data;
}
async createUser(
data: Omit<ApiUser, "id">
): Promise<ApiUser> {
const response = await this.request<ApiUser>("/users", {
method: "POST",
body: JSON.stringify(data),
});
return response.data;
}
}
// 使用例
const client = new ApiClient("https://api.example.com");
async function main(): Promise<void> {
try {
const user = await client.getUser(1);
console.log(`ユーザー名: ${user.name}`);
const newUser = await client.createUser({
name: "田中太郎",
email: "tanaka@example.com",
role: "member",
});
console.log(`作成されたユーザーID: ${newUser.id}`);
} catch (error) {
console.error("APIエラー:", error);
}
}
main();
まとめ
本記事では、TypeScriptの基礎から実践的な使い方までを解説しました。
- TypeScriptはJavaScriptに静的型付けを追加した言語で、バグの早期発見とIDEの強力な支援が得られる
- 基本型(string, number, boolean)から配列、タプル、オブジェクト型まで豊富な型表現が可能
- interfaceとtypeを使い分け、コードの意図を型で明確に表現できる
- ユニオン型とType Narrowingにより、型安全性と柔軟性を両立できる
- 実務では、APIクライアントやデータモデルの型定義が特に効果を発揮する
TypeScriptの型システムは奥が深いですが、まずは関数の引数・戻り値に型を書くことから始めてください。型がもたらす安全性を一度体験すると、JavaScriptに戻るのが難しくなるはずです。次のステップとして、ジェネリクスやユーティリティ型を学ぶことで、さらに表現力豊かな型定義が可能になります。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像