TypeScript Created: 2026/04/25 Updated: 2026/04/30

TypeScriptのクラス入門
class・constructor・継承の基本

TypeScriptのクラスの基本を初心者向けに解説。classの書き方、constructor、public/private、継承の仕組みまでシンプルに理解できます。

全8回シリーズ — 目次

1 TypeScriptとは

2 環境構築

3 基本の型

4 関数と型

5 インターフェイス

6 クラス

7 実践的な型

8 便利機能まとめ

🎯 この記事のゴール

TypeScriptのクラス構文を理解し、アクセス修飾子・継承・implements を使ったクラスベース設計ができるようになること。JavaやPHPの経験者には馴染み深い内容で、TypeScript独自の機能も合わせて学びます。

🔍 クラスを使うべきかどうか

TypeScriptでは、クラスよりも関数と interface の組み合わせで設計できる場面が多くあります。Reactなどのフロントエンドフレームワークはほぼクラスを使いません。一方、NestJSやORMライブラリ(TypeORM / Prismaなど)はクラスを多用します。「クラスが必要な場面で正しく使える」ことが目標です。

実行ファイルを用意しよう

Bunの場合

bash
touch classes.ts
bun --watch run classes.ts

Node.js + npm の場合

bash
touch src/classes.ts
npx tsc --watch  # 別ターミナルで起動
node dist/classes.js

クラスの基本 — プロパティと constructor

JavaScriptのクラスはES2015から導入されましたが、TypeScriptではプロパティや引数に型を付けられます。クラスを定義するとき、 プロパティの型宣言 を先頭に書くのが TypeScript 流です。

typescript
class User {
  // ① プロパティの型宣言(先頭にまとめて書く)
  id:    number;

  name:  string;

  email: string;


  // ② constructor で初期化
  constructor(id: number, name: string, email: string) {
    this.id    = id;
    this.name  = name;
    this.email = email;
  }

  // ③ メソッドにも戻り値の型を付ける
  greet(): string {
    return `こんにちは、${this.name}さん(ID: ${this.id})`;
  }

  toString(): string {
    return `[User] ${this.name} <${this.email}>`;
  }
}

const alice = new User(1, "Alice", "alice@example.com");
console.log(alice.greet());    // → こんにちは、Aliceさん(ID: 1)
console.log(alice.toString()); // → [User] Alice <alice@example.com>

Bunの場合

bash
bun run classes.ts
#こんにちは、Aliceさん(ID: 1)
#[User] Alice <alice@example.com>

Node.js + npm の場合

bash
npx tsc && node dist/classes.js
#こんにちは、Aliceさん(ID: 1)
#[User] Alice <alice@example.com>

アクセス修飾子 — public / private / protected

TypeScriptのクラスでは、プロパティやメソッドに アクセス修飾子 を付けて「どこからアクセスできるか」を制限できます。

public

どこからでもアクセス可能。デフォルト値なので省略しても同じ。明示的に書くと意図が伝わりやすい。

private

クラスの内部からのみアクセス可能。外部から直接変更させたくないデータに使う。

protected

クラス自身と継承したサブクラスからアクセス可能。継承を前提とした設計で使う。

typescript
class BankAccount {
  public  owner:   string;   // 外部からアクセスOK
  private balance: number;   // クラス内部のみ

  constructor(owner: string, initialBalance: number) {
    this.owner   = owner;
    this.balance = initialBalance;
  }

  // public メソッドを通じて private な残高を操作する
  public deposit(amount: number): void {
    if (amount <= 0) throw new Error("入金額は0より大きい値にしてください");
    this.balance += amount;
  }

  public withdraw(amount: number): void {
    if (amount > this.balance) throw new Error("残高不足です");
    this.balance -= amount;
  }

  // 残高は「見る」だけできる getter
  public getBalance(): number {
    return this.balance;
  }
}

const account = new BankAccount("Alice", 10000);
account.deposit(5000);
account.withdraw(3000);
console.log(account.owner);         // → Alice
console.log(account.getBalance());   // → 12000

// private プロパティへの直接アクセスはエラー
console.log(account.balance); // エラー: 'balance' は private プロパティ

Bunの場合

bash
bun run classes.ts
#Alice
#12000

Node.js + npm の場合

bash
npx tsc && node dist/classes.js
#Alice
#12000

コンストラクタ省略形(TypeScript の便利構文)

TypeScript ではconstructor の引数にアクセス修飾子を付けると、プロパティ宣言と初期化を同時に行えます。コードが大幅にすっきりします。

typescript
// 通常の書き方(冗長)
class PointVerbose {
  public x: number;
  public y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

// ↓ コンストラクタ省略形(引数に修飾子を付けるだけ)
class Point {
  constructor(public x: number, public y: number) {}


  distanceTo(other: Point): number {
    return Math.sqrt(
      (this.x - other.x) ** 2 + (this.y - other.y) ** 2
    );
  }

  toString(): string {
    return `(${this.x}, ${this.y})`;
  }
}

const p1 = new Point(0, 0);
const p2 = new Point(3, 4);
console.log(p1.toString());           // → (0, 0)
console.log(p1.distanceTo(p2));       // → 5

Bunの場合

bash
bun run classes.ts
#(0, 0)
#5

Node.js + npm の場合

bash
npx tsc && node dist/classes.js
#(0, 0)
#5

✨ 実務での使い分け

シンプルなデータクラスにはコンストラクタ省略形が便利です。一方、初期化ロジックが複雑な場合(バリデーション・変換処理など)は通常の書き方のほうが読みやすくなります。

継承 — extends で機能を引き継ぐ

extends を使うと親クラスのプロパティ・メソッドをすべて引き継いだ子クラスを作れます。super() で親クラスの constructor を呼び出す必要があります。

Animal(親クラス)

プロパティ

protected name : string

メソッド

public move (distance: number ): void

public toString ( ): string

↓ extends

Dog(子クラス)

追加メソッド

public bark (): void

Bird(子クラス)

追加メソッド

public fly (): void

typescript
class Animal {
  // protected: 子クラスからもアクセスできる
  constructor(protected name: string) {}

  move(distance: number = 0): void {
    console.log(`${this.name} が ${distance}m 移動した`);
  }

  toString(): string {
    return `Animal(${this.name})`;
  }
}

class Dog extends Animal {
  constructor(name: string, private breed: string) {
    super(name); // 親クラスのコンストラクタを呼ぶ(必須)
  }

  bark(): void {
    console.log(`${this.name}(${this.breed}): ワンワン!`);
  }

  // 親メソッドをオーバーライド
  override move(distance: number = 0): void {
    console.log(`${this.name} が走って ${distance}m 移動した`);
  }
}

class Bird extends Animal {
  constructor(name: string) {
    super(name);
  }

  fly(altitude: number): void {
    console.log(`${this.name} が ${altitude}m の高さを飛んでいる`);
  }
}

const dog  = new Dog("ポチ", "柴犬");
const bird = new Bird("ピーコ");

dog.bark();        // → ポチ(柴犬): ワンワン!
dog.move(20);      // → ポチ が走って 20m 移動した(オーバーライド後)
bird.fly(100);     // → ピーコ が 100m の高さを飛んでいる
bird.move(10);     // → ピーコ が 10m 移動した(親クラスのまま)

Bunの場合

bash
bun run classes.ts
#ポチ(柴犬): ワンワン!
#ポチ が走って 20m 移動した
#ピーコ が 100m の高さを飛んでいる
#ピーコ が 10m 移動した

Node.js + npm の場合

bash
npx tsc && node dist/classes.js
#ポチ(柴犬): ワンワン!
#ポチ が走って 20m 移動した
#ピーコ が 100m の高さを飛んでいる
#ピーコ が 10m 移動した

✨ override キーワード

TypeScript 4.3以降ではoverride キーワードを付けることで「親クラスのメソッドを意図的に上書きしている」と明示できます。親クラスにそのメソッドが存在しない場合はコンパイルエラーになるため、リファクタリング時の事故を防げます。

implements — interface との組み合わせ

クラスにimplements を付けると、そのクラスが指定した interface の「契約」を必ず実装していることを TypeScript が保証します。 「何ができるか」を interface で定義し、「どうやるか」をクラスで実装する のが設計の基本です。

typescript
// 「通知を送る」という契約(インターフェース)
interface Notifier {
  send(to: string, message: string): void;
  name: string;
}

// メール通知の実装
class EmailNotifier implements Notifier {
  name = "Email";

  send(to: string, message: string): void {
    console.log(`[Email → ${to}] ${message}`);
  }
}

// Slack 通知の実装
class SlackNotifier implements Notifier {
  name = "Slack";

  send(to: string, message: string): void {
    console.log(`[Slack → #${to}] ${message}`);
  }
}

// interface 型で受け取れば、実装の詳細に依存しない
function notifyAll(notifiers: Notifier[], message: string): void {
  notifiers.forEach((n) => {
    n.send("admin", `[${n.name}] ${message}`);
  });
}

notifyAll(
  [new EmailNotifier(), new SlackNotifier()],
  "デプロイが完了しました"
);

// implements を付けても必要なメソッドがないとエラー
class BrokenNotifier implements Notifier {
  name = "Broken";
  // send メソッドがない → コンパイルエラー

}

Bunの場合

bash
bun run classes.ts
#[Email → admin] [Email] デプロイが完了しました
#[Slack → #admin] [Slack] デプロイが完了しました

Node.js + npm の場合

bash
npx tsc && node dist/classes.js
#[Email → admin] [Email] デプロイが完了しました
#[Slack → #admin] [Slack] デプロイが完了しました

クラス関連キーワード 早見表

キーワード 意味 主な使いどころ
public どこからでもアクセス可 外部に公開するプロパティ・メソッド(デフォルト)
private クラス内部のみアクセス可 残高・パスワードなど外部から直接変更させたくないデータ
protected クラス本体 + 継承クラスのみ 子クラスで使う共通プロパティ
readonly 初期化後は変更不可 IDなど変わってほしくない値
extends 親クラスを継承 共通機能を持つ基底クラスから派生させるとき
implements interface の契約を実装 「何ができるか」を保証したいとき
override 親メソッドを意図的に上書き 子クラスで親メソッドの動作を変えるとき
super() 親クラスのコンストラクタを呼ぶ extends しているクラスの constructor で必須

🔍 クラスを使うべきかどうか

TypeScriptでは、クラスよりも関数と interface の組み合わせで設計できる場面が多くあります。Reactなどのフロントエンドフレームワークはほぼクラスを使いません。一方、NestJSやORMライブラリ(TypeORM / Prismaなど)はクラスを多用します。「クラスが必要な場面で正しく使える」ことが目標です。

📌 第6回のまとめ

クラスの基本構文・コンストラクタ省略形・アクセス修飾子(public /private /protected )・継承(extends )・interface との組み合わせ(implements )を学びました。次回は Union型・literal型・型ガード・型推論といった「TypeScriptらしい書き方」を一気に学ぶ実践回です。

📝 ▶ 次のステップ

次は「 」を解説します
TypeScriptの実践的な型\nunion型・型ガード・型推論を解説
TypeScript

TypeScriptの実践的な型 union型・型ガード・型推論を解説

TypeScriptの実践的な型を解説。union型・literal型・型ガード・型推論の使い方を初心者向けにわかりやすく紹介します。