TypeScript | 関数・クラス・ジェネリクス:クラス設計 – クラスの基本構文復習

TypeScript TypeScript
スポンサーリンク

クラスってそもそも何者?「設計図」と「new」の関係から整理する

まずイメージからいきます。

クラスはざっくり言うと、

「オブジェクトを量産するための設計図」

です。

設計図(class)を書いておいて、
new を使って「実物(インスタンス)」を何個でも作る、という世界観です。

class User {
}

const u1 = new User();
const u2 = new User();
TypeScript

この時点ではまだ「中身のない設計図」なので、
何もできません。

ここに「状態(プロパティ)」と「振る舞い(メソッド)」を足していくのが、
クラスの基本構文です。


プロパティとコンストラクタ:インスタンスの「状態」をどう持たせるか

プロパティ(フィールド)の基本

クラスの中で「変数のようなもの」を宣言すると、
それがインスタンスのプロパティになります。

class User {
  id: number;
  name: string;
}

const u = new User();
u.id = 1;
u.name = "Taro";
TypeScript

ここで idname は、
User インスタンスが持つ「状態」です。

TypeScript なので、
id: number; のように型を書けるのがポイントです。

コンストラクタ:作られた瞬間に何をするか

constructor は「new された瞬間に呼ばれる特別なメソッド」です。

class User {
  id: number;
  name: string;

  constructor(id: number, name: string) {
    this.id = id;
    this.name = name;
  }
}

const u = new User(1, "Taro");
console.log(u.id);   // 1
console.log(u.name); // "Taro"
TypeScript

ここで大事なのは this です。

  • this.id は「このインスタンスの id プロパティ」
  • this.name は「このインスタンスの name プロパティ」

を指します。

「コンストラクタの引数 → this.プロパティ に代入する」
これが、インスタンスに初期値をセットする基本パターンです。

コンストラクタの省略記法(アクセス修飾子付き引数)

TypeScript には、
コンストラクタでよくやるこのパターンを短く書くための記法があります。

class User {
  constructor(
    public id: number,
    public name: string
  ) {}
}

const u = new User(1, "Taro");
console.log(u.id);   // 1
console.log(u.name); // "Taro"
TypeScript

public id: number のように書くと、

  • プロパティ宣言
  • コンストラクタ引数
  • this.id = id の代入

をまとめてやってくれます。

「プロパティを宣言して、コンストラクタで代入して…」が
面倒に感じてきたら、この書き方を覚えておくとかなり楽になります。


メソッド:インスタンスの「振る舞い」を定義する

メソッドの基本形

クラスの中で関数を書くと、それがメソッドになります。

class User {
  constructor(
    public id: number,
    public name: string
  ) {}

  greet(): void {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const u = new User(1, "Taro");
u.greet(); // Hello, I'm Taro
TypeScript

ここでも this が重要です。

this.name は「このインスタンスの name」を指します。

「プロパティ=名詞(状態)、メソッド=動詞(振る舞い)」
というイメージで捉えると、設計しやすくなります。

メソッドにも型を書く

メソッドも普通の関数と同じように、
引数と戻り値に型を書きます。

class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }

  logResult(value: number): void {
    console.log("result:", value);
  }
}

const c = new Calculator();
const result = c.add(1, 2); // number
c.logResult(result);
TypeScript
  • addnumber を返す
  • logResult は何も返さない(void

ということが、型から分かります。


アクセス修飾子:public / private / protected の基本

public:どこからでも触っていい

何も書かない、または public を付けたプロパティ・メソッドは、
どこからでもアクセスできます。

class User {
  public id: number;
  public name: string;

  public constructor(id: number, name: string) {
    this.id = id;
    this.name = name;
  }

  public greet(): void {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const u = new User(1, "Taro");
console.log(u.id);   // OK
u.greet();           // OK
TypeScript

アクセス修飾子を省略すると public 扱いになります。

private:クラスの外からは触らせない

private を付けると、
そのクラスの外からはアクセスできなくなります。

class User {
  constructor(
    private id: number,
    private name: string
  ) {}

  public greet(): void {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const u = new User(1, "Taro");
// console.log(u.id);   // エラー:private なので外から見えない
u.greet();             // OK
TypeScript

「外から勝手にいじらせたくない状態」は private にする
というのが、クラス設計の基本的な考え方です(カプセル化)。

protected:サブクラスからは触れる

protected は「外からはダメだけど、継承したクラスからは OK」という修飾子です。

class Animal {
  constructor(
    protected name: string
  ) {}

  protected move(distance: number): void {
    console.log(`${this.name} moved ${distance}m`);
  }
}

class Dog extends Animal {
  public bark(): void {
    console.log(`${this.name} says ワン!`); // protected なので OK
    this.move(5);                           // これも OK
  }
}

const d = new Dog("Pochi");
// console.log(d.name); // エラー:外からは見えない
d.bark();
TypeScript

継承を使うときに効いてくる修飾子ですが、
最初は「public と private が分かれば十分」です。


継承:クラスを「ベースにして拡張する」

extends でクラスを引き継ぐ

extends を使うと、
あるクラスをベースにして新しいクラスを作れます。

class Animal {
  constructor(
    public name: string
  ) {}

  move(distance: number): void {
    console.log(`${this.name} moved ${distance}m`);
  }
}

class Dog extends Animal {
  bark(): void {
    console.log("ワン!");
  }
}

const d = new Dog("Pochi");
d.move(10); // 親クラスのメソッド
d.bark();   // 子クラスで追加したメソッド
TypeScript

DogAnimal を継承しているので、

  • name プロパティ
  • move メソッド

をそのまま使えます。

super で親クラスのコンストラクタを呼ぶ

親クラスにコンストラクタがある場合、
子クラスのコンストラクタでは super(...) を呼び出します。

class Animal {
  constructor(
    public name: string
  ) {}
}

class Dog extends Animal {
  constructor(
    name: string,
    public breed: string
  ) {
    super(name); // 親クラスの constructor(name) を呼ぶ
  }

  info(): void {
    console.log(`${this.name} (${this.breed})`);
  }
}

const d = new Dog("Pochi", "Shiba");
d.info(); // Pochi (Shiba)
TypeScript

super(name) が、
「親クラスのコンストラクタを呼んで、name を初期化する」
という意味です。


まとめ:クラスの基本構文を自分の言葉で整理する

ここまでを、あなた自身の言葉でまとめるとこうなります。

  • class は「オブジェクトの設計図」
  • プロパティで「状態」を、メソッドで「振る舞い」を定義する
  • constructornew された瞬間に呼ばれ、this に初期値を入れる
  • public / private / protected で「どこから触れるか」を決める
  • extends でクラスを継承し、super で親のコンストラクタを呼ぶ

もしよければ、今のあなたの身近なもの(例:本、ユーザー、注文など)を題材にして、
小さなクラスを1つ書いてみてください。

「プロパティは何を持たせる?」
「どんなメソッドがあると“それっぽい”?」

と考えながら書くと、
クラスの構文がただの文法ではなく、
“自分の頭の中のモデルをコードにする道具”として見えてきます。

タイトルとURLをコピーしました