TypeScriptはJavaScriptに静的型付けを加えた言語として、2026年現在ほぼ全てのモダンなWeb開発プロジェクトで採用されています。この記事では、TypeScript初心者から中級者に向けて、型システムの基礎から実践的なNext.js・React開発での活用まで徹底解説します。
TypeScriptとは?JavaScriptとの違い
TypeScriptはMicrosoftが開発したJavaScriptのスーパーセットです。コンパイル時に型エラーを検出できるため、大規模なコードベースでのバグを事前に防ぐことができます。
TypeScriptを使うメリット
- ✅ 型安全性:コンパイル時にバグを検出
- ✅ IDE補完の強化:IntelliSenseで開発効率UP
- ✅ リファクタリングが安全:型情報で影響範囲が明確
- ✅ チーム開発に最適:コードの意図が型で伝わる
TypeScriptの基本型システム
プリミティブ型
// 基本的な型アノテーション
const name: string = "田中太郎";
const age: number = 25;
const isActive: boolean = true;
const nothing: null = null;
const notDefined: undefined = undefined;
// 配列型
const numbers: number[] = [1, 2, 3];
const strings: Array = ["a", "b", "c"];
// タプル型
const tuple: [string, number] = ["hello", 42];
// any型(使用は最小限に)
const anything: any = "anything goes";
インターフェースと型エイリアス
// インターフェースの定義
interface User {
id: number;
name: string;
email: string;
role: "admin" | "user" | "moderator"; // ユニオン型
createdAt: Date;
bio?: string; // オプショナルプロパティ
readonly uuid: string; // 読み取り専用
}
// 型エイリアス
type ApiResponse = {
data: T;
status: number;
message: string;
timestamp: string;
};
// 使用例
const user: User = {
id: 1,
name: "田中太郎",
email: "tanaka@example.com",
role: "admin",
createdAt: new Date(),
uuid: "550e8400-e29b-41d4-a716-446655440000"
};
const response: ApiResponse = {
data: user,
status: 200,
message: "Success",
timestamp: new Date().toISOString()
};
ジェネリクス(Generics)
// ジェネリック関数
function getFirst(array: T[]): T | undefined {
return array[0];
}
const firstNumber = getFirst([1, 2, 3]); // type: number | undefined
const firstString = getFirst(["a", "b"]); // type: string | undefined
// ジェネリッククラス
class Stack {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
get size(): number {
return this.items.length;
}
}
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.pop()); // 2
実践的なTypeScript:React・Next.jsでの活用
Reactコンポーネントの型定義
import React, { FC, useState, useCallback } from 'react';
// Propsの型定義
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
disabled?: boolean;
children?: React.ReactNode;
}
// FC(Function Component)で型付け
const Button: FC = ({
label,
onClick,
variant = "primary",
disabled = false,
children
}) => {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{children || label}
</button>
);
};
// カスタムフックの型付け
function useCounter(initialValue: number = 0) {
const [count, setCount] = useState<number>(initialValue);
const increment = useCallback(() => setCount(c => c + 1), []);
const decrement = useCallback(() => setCount(c => c - 1), []);
const reset = useCallback(() => setCount(initialValue), [initialValue]);
return { count, increment, decrement, reset };
}
Next.jsでのAPI Routeの型付け
// app/api/users/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server';
interface UserCreateRequest {
name: string;
email: string;
role: "admin" | "user";
}
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const page = Number(searchParams.get('page') || 1);
try {
const users = await prisma.user.findMany({
skip: (page - 1) * 10,
take: 10,
orderBy: { createdAt: 'desc' }
});
return NextResponse.json({
data: users,
page,
total: await prisma.user.count()
});
} catch (error) {
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
const body: UserCreateRequest = await request.json();
if (!body.name || !body.email) {
return NextResponse.json(
{ error: 'Name and email are required' },
{ status: 400 }
);
}
const user = await prisma.user.create({
data: body
});
return NextResponse.json(user, { status: 201 });
}
TypeScriptの高度な型テクニック
Utility Types
interface Product {
id: number;
name: string;
price: number;
description: string;
stock: number;
}
// Partial:すべてのプロパティをオプショナルに
type UpdateProduct = Partial<Product>;
// Required:すべてのプロパティを必須に
type RequiredProduct = Required<Product>;
// Pick:特定のプロパティのみ選択
type ProductSummary = Pick<Product, "id" | "name" | "price">;
// Omit:特定のプロパティを除外
type CreateProduct = Omit<Product, "id">;
// Readonly:すべてのプロパティを読み取り専用に
type ImmutableProduct = Readonly<Product>;
// Record:キーと値の型を指定したオブジェクト
type CategoryProducts = Record<string, Product[]>;
TypeScript設定ファイル(tsconfig.json)のベストプラクティス
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true, // 厳格モードを有効化
"noImplicitAny": true, // 暗黙的なany型を禁止
"strictNullChecks": true, // nullチェックを厳格に
"noUnusedLocals": true, // 未使用のローカル変数を検出
"noUnusedParameters": true, // 未使用のパラメータを検出
"noImplicitReturns": true, // 全コードパスでreturnを強制
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
まとめ:TypeScriptで型安全な開発を実現しよう
TypeScriptをマスターすることで、開発効率と品質が大幅に向上します。最初は型エラーに戸惑うかもしれませんが、慣れると型の恩恵を強く感じるようになります。Reactエコシステムとの相性も抜群なので、Next.js開発と組み合わせて実践的なプロジェクトで習得を進めましょう。