TypeScriptとは?JavaScriptとの違いと学ぶ理由
TypeScriptは、Microsoftが開発したJavaScriptのスーパーセット(上位互換言語)です。JavaScriptに静的型付けとクラスベースのオブジェクト指向機能を追加しており、2026年現在では多くの企業でフロントエンド・バックエンド開発の標準として採用されています。
TypeScriptを学ぶべき3つの理由
- バグを事前に検出:型チェックにより、実行前にエラーを発見できる
- IDE補完が強力:VSCodeとの相性が抜群で開発生産性が向上する
- 業界標準:React、Next.js、NestJSなど主要フレームワークが採用
TypeScriptの環境構築
# TypeScriptのインストール
npm install -g typescript
tsc --version
# プロジェクトの初期化
mkdir my-ts-project && cd my-ts-project
npm init -y
npm install --save-dev typescript @types/node ts-node
# tsconfig.jsonの生成
npx tsc --init
推奨のtsconfig.json設定:
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
TypeScriptの基本型と型システム
プリミティブ型
// プリミティブ型
let name: string = "Tech Athletes";
let age: number = 25;
let isActive: boolean = true;
let nothing: null = null;
let undef: undefined = undefined;
let big: bigint = 100n;
let sym: symbol = Symbol("id");
// 配列型
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];
// タプル型
let tuple: [string, number] = ["Alice", 30];
// any型(使用は最小限に)
let anything: any = "これは何でも入る";
// unknown型(anyより安全)
let value: unknown = "文字列かも?";
if (typeof value === "string") {
console.log(value.toUpperCase());
}
// never型
function throwError(msg: string): never {
throw new Error(msg);
}
インターフェースと型エイリアス
// インターフェース
interface User {
id: number;
name: string;
email: string;
age?: number; // オプショナル
readonly createdAt: Date; // 読み取り専用
}
// 型エイリアス
type UserRole = "admin" | "editor" | "viewer";
type Status = "active" | "inactive" | "pending";
// ジェネリクス
interface ApiResponse<T> {
data: T;
status: number;
message: string;
timestamp: Date;
}
// 使用例
const userResponse: ApiResponse<User> = {
data: {
id: 1,
name: "山田太郎",
email: "yamada@example.com",
createdAt: new Date()
},
status: 200,
message: "success",
timestamp: new Date()
};
ユニオン型・インターセクション型
// ユニオン型(AまたはB)
type StringOrNumber = string | number;
type Result = "success" | "error" | "loading";
// インターセクション型(AかつB)
interface Timestamped {
createdAt: Date;
updatedAt: Date;
}
interface SoftDeletable {
deletedAt: Date | null;
}
type Entity = Timestamped & SoftDeletable;
// 型ガード
function isString(value: unknown): value is string {
return typeof value === "string";
}
// Discriminated Union(判別可能なユニオン)
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "triangle"; base: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return (shape.base * shape.height) / 2;
}
}
高度な型パターン(Utility Types)
// TypeScript組み込みUtility Types
interface Product {
id: number;
name: string;
price: number;
category: string;
description: string;
}
// Partial: 全プロパティをオプショナルに
type PartialProduct = Partial<Product>;
// Required: 全プロパティを必須に
type RequiredProduct = Required<Product>;
// Readonly: 全プロパティを読み取り専用に
type ReadonlyProduct = Readonly<Product>;
// Pick: 特定のプロパティのみ選択
type ProductPreview = Pick<Product, "id" | "name" | "price">;
// Omit: 特定のプロパティを除外
type CreateProductDto = Omit<Product, "id">;
// Record: キーと値の型を指定したオブジェクト型
type ProductMap = Record<string, Product>;
// Exclude / Extract
type NonString = Exclude<string | number | boolean, string>; // number | boolean
type OnlyString = Extract<string | number | boolean, string>; // string
// ReturnType: 関数の戻り値の型を取得
function getUser() {
return { id: 1, name: "Alice" };
}
type UserType = ReturnType<typeof getUser>;
// Parameters: 関数のパラメータ型を取得
function createUser(name: string, age: number) {}
type CreateUserParams = Parameters<typeof createUser>;
React + TypeScriptの実践パターン
// React.FC vs 関数コンポーネントの型定義
import React, { useState, useCallback, useMemo } from "react";
// Propsの型定義
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
disabled?: boolean;
children?: React.ReactNode;
}
// コンポーネントの定義
const Button: React.FC<ButtonProps> = ({
label,
onClick,
variant = "primary",
disabled = false,
children
}) => {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{children ?? label}
</button>
);
};
// カスタムフックの型定義
interface UseCounterReturn {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
function useCounter(initialValue: number = 0): UseCounterReturn {
const [count, setCount] = useState<number>(initialValue);
const increment = useCallback(() => setCount(prev => prev + 1), []);
const decrement = useCallback(() => setCount(prev => prev - 1), []);
const reset = useCallback(() => setCount(initialValue), [initialValue]);
return { count, increment, decrement, reset };
}
// ジェネリクスを使ったAPIフック
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json() as Promise<T>;
}
まとめ:TypeScriptでより堅牢なコードを書こう
TypeScriptは学習コストがあるものの、大規模プロジェクトではそのメリットが大きく上回ります。
- ✅ 型システムにより実行前にバグを検出できる
- ✅ IDEのサポートが充実し開発効率が上がる
- ✅ コードの可読性・保守性が向上する
- ✅ チーム開発での品質担保に役立つ
まずは既存のJavaScriptプロジェクトに少しずつTypeScriptを導入してみましょう。allowJs: trueオプションを使えば段階的な移行も可能です。