【初心者向け】TypeScript入門|JavaScriptとの違いと始め方完全ガイド

kento_morota 18分で読めます

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との主な違い

項目JavaScriptTypeScript
型システム動的型付け静的型付け(型推論あり)
コンパイル不要(インタプリタ)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の使い分け

機能interfacetype
オブジェクトの型定義
拡張(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に戻るのが難しくなるはずです。次のステップとして、ジェネリクスやユーティリティ型を学ぶことで、さらに表現力豊かな型定義が可能になります。

#TypeScript#初心者#JavaScript
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

起業準備に役立つ情報、もっとありますよ。

まずは話だけ聞いてもらう