TypeScript完全入門【2026年版】型システムの基礎から応用パターン・React/Next.jsとの連携まで

TypeScriptとは?JavaScriptとの違いと学ぶ理由

TypeScriptは、Microsoftが開発したJavaScriptのスーパーセット(上位互換言語)です。JavaScriptに静的型付けクラスベースのオブジェクト指向機能を追加しており、2026年現在では多くの企業でフロントエンド・バックエンド開発の標準として採用されています。

TypeScriptを学ぶべき3つの理由

  1. バグを事前に検出:型チェックにより、実行前にエラーを発見できる
  2. IDE補完が強力:VSCodeとの相性が抜群で開発生産性が向上する
  3. 業界標準: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オプションを使えば段階的な移行も可能です。

投稿者 kasata

IT企業でエンジニアとして勤務後、テクノロジー情報メディア「Tech Athletes(テック・アスリート)」を運営。プログラミング、クラウドインフラ(AWS/GCP/Azure)、AI活用、Webサービス開発を専門とする。エンジニア・ビジネスパーソン向けに、実際に使ってみた経験をもとに信頼できる技術情報を発信中。資格:AWS認定ソリューションアーキテクト、Python 3 エンジニア認定試験合格。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です