TypeScriptのクラスと継承は、オブジェクト指向プログラミングの概念をサポートしており、再利用性や拡張性の高いコードを書くために非常に重要です。クラスを使うことで、オブジェクトの設計をシンプルにし、共通の振る舞いをもつオブジェクトを効率的に作成することができます。
この章では、TypeScriptにおけるクラスの定義から、クラス間での継承、アクセス修飾子の使い方まで、オブジェクト指向プログラミングの基礎を学びます。
5.1 クラスの基本
TypeScriptのクラスは、オブジェクトの設計図です。クラスには、プロパティとメソッドを定義して、オブジェクトの状態と動作を記述することができます。TypeScriptでは、JavaScriptのES6(ECMAScript 2015)で導入されたクラスを基盤にしつつ、型注釈を使って安全性を高めています。
5.1.1 クラスの定義
TypeScriptでクラスを定義するには、class
キーワードを使います。クラス内には、プロパティやメソッドを定義してオブジェクトの構造を設計します。
class Person {
name: string;
age: number;
// コンストラクタ: クラスのインスタンスが作られるときに呼ばれる
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// メソッド: クラスに属する関数
greet(): string {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
}
}
// クラスのインスタンスを作成
let john = new Person("John", 30);
console.log(john.greet()); // "Hello, my name is John and I am 30 years old."
この例では、Person
というクラスを定義し、name
とage
というプロパティ、そしてgreet()
というメソッドを持っています。クラスのインスタンスを作成するためには、new
キーワードを使ってインスタンス化します。
5.1.2 コンストラクタ
コンストラクタは、クラスからオブジェクトが作られるときに自動的に呼び出される特別なメソッドです。TypeScriptでは、コンストラクタ内でオブジェクトのプロパティを初期化することが一般的です。
コンストラクタは、次のようにconstructor
という名前で定義します。
class Car {
brand: string;
model: string;
constructor(brand: string, model: string) {
this.brand = brand;
this.model = model;
}
displayInfo(): string {
return `This car is a ${this.brand} ${this.model}.`;
}
}
let myCar = new Car("Toyota", "Corolla");
console.log(myCar.displayInfo()); // "This car is a Toyota Corolla."
この例では、Car
クラスにbrand
とmodel
というプロパティが定義され、コンストラクタでその値が設定されます。インスタンスが作成されるたびに、コンストラクタが実行され、オブジェクトの初期化が行われます。
5.2 クラスのメソッド
クラス内で定義されたメソッドは、インスタンス化されたオブジェクトに対して呼び出すことができます。メソッドはクラスの動作を定義し、クラスのプロパティにアクセスしてその値を操作したり表示したりすることができます。
5.2.1 メソッドの定義
クラスメソッドは、クラス内で通常の関数のように定義します。メソッド内でクラスのプロパティにアクセスするには、this
キーワードを使います。
class Rectangle {
width: number;
height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
// 面積を計算するメソッド
calculateArea(): number {
return this.width * this.height;
}
}
let rect = new Rectangle(10, 5);
console.log(rect.calculateArea()); // 50
この例では、Rectangle
クラスにcalculateArea()
というメソッドが定義されています。このメソッドは、this.width
とthis.height
にアクセスし、長方形の面積を計算します。
5.3 アクセス修飾子
アクセス修飾子は、クラスのプロパティやメソッドへのアクセスを制御するために使います。TypeScriptでは、次の3つのアクセス修飾子が用意されています。
public
: デフォルトのアクセス修飾子で、クラスの外部からでもアクセス可能。private
: クラス内からのみアクセス可能で、クラス外からはアクセスできない。protected
: クラス内および継承したサブクラスからアクセス可能。
5.3.1 public
修飾子
public
は、デフォルトのアクセス修飾子です。public
で宣言されたプロパティやメソッドは、クラス外からも自由にアクセスできます。
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
public speak(): void {
console.log(`${this.name} makes a noise.`);
}
}
let cat = new Animal("Cat");
cat.speak(); // "Cat makes a noise."
この例では、name
プロパティとspeak()
メソッドがpublic
として宣言されており、クラスの外部からもアクセスできます。
5.3.2 private
修飾子
private
で宣言されたプロパティやメソッドは、クラス内からのみアクセス可能です。外部からの直接アクセスを防ぐことで、クラスの内部構造を隠蔽できます。
class BankAccount {
private balance: number;
constructor(initialBalance: number) {
this.balance = initialBalance;
}
public deposit(amount: number): void {
this.balance += amount;
}
public getBalance(): number {
return this.balance;
}
}
let account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// account.balance = 0; // エラー: balanceはprivateでアクセスできない
この例では、balance
プロパティがprivate
として宣言されています。そのため、クラス外から直接balance
にアクセスすることはできませんが、deposit()
やgetBalance()
を通じて間接的に操作できます。
5.3.3 protected
修飾子
protected
は、クラス内およびそのクラスを継承したサブクラスからアクセス可能な修飾子です。外部からはアクセスできませんが、サブクラスで利用したいプロパティやメソッドに使用します。
class Vehicle {
protected speed: number;
constructor(speed: number) {
this.speed = speed;
}
protected accelerate(amount: number): void {
this.speed += amount;
}
}
class Car extends Vehicle {
private brand: string;
constructor(brand: string, speed: number) {
super(speed);
this.brand = brand;
}
public speedUp(): void {
this.accelerate(10);
console.log(`${this.brand} is now going at ${this.speed} km/h.`);
}
}
let myCar = new Car("Toyota", 60);
myCar.speedUp(); // "Toyota is now going at 70 km/h."
// myCar.speed = 100; // エラー: speedはprotectedでアクセスできない
この例では、Vehicle
クラスのspeed
プロパティとaccelerate()
メソッドがprotected
として宣言されています。そのため、サブクラスであるCar
クラス内からはアクセス可能ですが、外部からはアクセスできません。
5.4 クラスの継承
継承は、オブジェクト指向プログラミングの重要な概念の一つで、既存のクラスを基にして新しいクラスを作成することができます。TypeScriptでは、extends
キーワードを使ってクラスを継承します。
5.4.1 クラスの基本的な継承
クラスを継承することで、既存のクラス(親クラス)のプロパティやメソッドを再利用し、サブクラスでさらに新しい機能を追加できます。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name); // 親クラスのコンストラクタを呼び出す
}
// 親クラスのメソッドを上書き(オーバーライド)
makeSound(): void {
console.log(`${this.name} barks.`);
}
}
let dog = new Dog("Rex");
dog.makeSound(); // "Rex barks."
この例では、Dog
クラスがAnimal
クラスを継承しています。super()
を使って親クラスのコンストラクタを呼び出し、親クラスのプロパティやメソッドを利用しつつ、新しいメソッドを追加しています。
5.4.2 メソッドのオーバーライド
サブクラスは、親クラスで定義されたメソッドをオーバーライド(上書き)することができます。サブクラス内で同じ名前のメソッドを定義すると、そのメソッドは親クラスのメソッドを上書きし、サブクラス独自の動作を持つようになります。
class Bird extends Animal {
constructor(name: string) {
super(name);
}
// 親クラスのmakeSoundメソッドをオーバーライド
makeSound(): void {
console.log(`${this.name} chirps.`);
}
}
let bird = new Bird("Sparrow");
bird.makeSound(); // "Sparrow chirps."
この例では、Bird
クラスが親クラスAnimal
のmakeSound()
メソッドをオーバーライドして、鳥の鳴き声を定義しています。
5.5 クラスとインターフェースの関係
クラスとインターフェースは、TypeScriptでよく一緒に使われます。インターフェースを使うことで、クラスがどのようなプロパティやメソッドを実装すべきかを指定できます。これにより、クラスの構造が明確になり、コードの再利用性や保守性が向上します。
5.5.1 クラスにインターフェースを実装する
クラスがインターフェースを**実装(implement)**する場合、implements
キーワードを使います。インターフェースで定義されたプロパティやメソッドを、クラス内で必ず実装しなければなりません。
interface Movable {
move(): void;
}
class Car implements Movable {
move(): void {
console.log("The car is moving.");
}
}
let myCar = new Car();
myCar.move(); // "The car is moving."
この例では、Movable
インターフェースがmove()
というメソッドを定義しており、Car
クラスがそれを実装しています。
まとめ
この章では、TypeScriptにおけるクラスと継承について詳しく学びました。クラスを使うことでオブジェクト指向プログラミングの概念を活用でき、再利用性や保守性が向上します。また、アクセス修飾子を使ったデータの隠蔽や、継承によるクラスの拡張も学びました。これにより、より複雑で高度なアプリケーションを構築するための基盤が整いました。