TypeScriptの勉強:クラスと継承と抽象とインターフェースとジェネリクス
はじめに
インフラエンジニアの私がAWS CDKの為の勉強の為の備忘録です。クラスとオブジェクトまで勉強しました。
TypeScriptの勉強: クラスとオブジェクト
1. クラス (Class)
定義: クラスは、オブジェクトの「設計図」や「型」です。
例:
class Car {
// プロパティ
brand: string;
year: number;
// コンストラクタ: オブジェクトを初期化
constructor(brand: string, year: number) {
this.brand = brand;
this.year = year;
}
// メソッド
displayCarInfo() {
console.log(`This car is a ${this.year} ${this.brand}`);
}
}
const toyota = new Car("Toyota", 2022);
toyota.displayCarInfo(); // This car is a 2022 Toyota
ここで、Car
クラスはbrand
とyear
というプロパティを持っており、displayCarInfo
というメソッドでその情報を表示します。
2. 継承 (Inheritance)
定義: 継承を使うと、一つのクラス(基底クラス)の属性やメソッドを別のクラス(派生クラス)が引き継ぐことができます。
例:
class ElectricCar extends Car {
batteryLife: number;
constructor(brand: string, year: number, batteryLife: number) {
super(brand, year); // 親クラスのコンストラクタを呼ぶ
this.batteryLife = batteryLife;
}
displayBatteryLife() {
console.log(`Battery life is: ${this.batteryLife} hours`);
}
}
const tesla = new ElectricCar("Tesla", 2022, 10);
tesla.displayCarInfo(); // This car is a 2022 Tesla
tesla.displayBatteryLife(); // Battery life is: 10 hours
3. 抽象 (Abstract)
定義: 抽象クラスは、直接インスタンス化することができないクラスです。これは、一般的な概念や基盤としての役割を果たすクラスで、具体的な実装の一部が欠けているため直接使用することはできません。このようなクラスは、他のクラスが実装や継承するための基礎として使用されます。
具体的には、抽象クラス内の一部のメソッドは抽象メソッドとして定義され、これは実装が存在しないメソッドです。継承するクラスはこれらの抽象メソッドを必ず実装する必要があります。
例:
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
// 通常のメソッド
display(): void {
console.log(`This is a ${this.name}`);
}
// 抽象メソッド
abstract makeSound(): void;
}
class Dog extends Animal {
// 抽象メソッドの実装
makeSound() {
console.log("Woof! Woof!");
}
}
const myDog = new Dog("Buddy");
myDog.display(); // This is a Buddy
myDog.makeSound(); // Woof! Woof!
なぜ抽象クラスを使用するのか?
再利用: 共通のロジックやプロパティを基底の抽象クラスに持たせ、複数のクラスで再利用することができます。
契約の提供: 抽象クラスは派生クラスに対して特定の構造やメソッドの実装を強制することができます。
拡張性: 新しいメソッドやプロパティを抽象クラスに追加すると、すべての派生クラスにその変更が反映されます。
4. インターフェース (Interface)
定義: TypeScriptのインターフェースは、オブジェクトの型を指定するための強力な方法であり、特定のプロパティやメソッドを持つことを強制する「契約」として機能します。クラスやオブジェクトが持つべき構造を定義することができます。
例:
interface Person {
firstName: string;
lastName: string;
}
function greet(person: Person) {
console.log(`Hello, ${person.firstName} ${person.lastName}`);
}
let user = { firstName: "Taro", lastName: "Yamada" };
greet(user); // Hello, Taro Yamada
PersonインターフェースはfirstNameとlastNameという2つのプロパティを持つことを要求しています。そして、greet関数はPerson型の引数を受け取ります。
インターフェースとクラス:
インターフェースは、特定のクラスが特定のメソッドやプロパティを持つことを強制することもできます。この特性を使って、クラスに一貫性を持たせることができます。
interface AnimalSound {
sound(): void;
}
class Cat implements AnimalSound {
sound() {
console.log("にゃーん");
}
}
class Dog implements AnimalSound {
sound() {
console.log("わんわん");
}
}
const myCat = new Cat();
myCat.sound(); // にゃーん
const myDog = new Dog();
myDog.sound(); // わんわん
なぜインターフェースを使用するのか?
一貫性: インターフェースは、クラスやオブジェクトが一貫した構造を持つことを保証します。
拡張性: 既存のインターフェースに新しいメソッドやプロパティを追加することで、それを実装するすべてのクラスが新しい変更を取り込むことが求められます。
再利用性: 同じインターフェースを多くのクラスで実装することができ、共通の契約を再利用することができます。
可読性: コードの読者はインターフェースを見るだけで、該当するクラスやオブジェクトがどのような構造や動作を持つべきかをすぐに理解することができます。
5. ジェネリクス (Generics)
定義: ジェネリクスは、関数やクラスに型を「パラメータ」として渡すことで、再利用性を高める方法です。
なぜ便利なのか?
型の安全性: 間違った型のデータを扱おうとすると、TypeScriptはエラーを出してくれます。
再利用性: 一つの関数やクラスで、様々な型をサポートすることができます。
例:
function getArray<T>(items: T[]): T[] {
return new Array<T>().concat(items);
}
let myNumArr = getArray<number>([100, 200, 300]);
let myStrArr = getArray<string>(["Hello", "World"]);
console.log(myNumArr); // [100, 200, 300]
console.log(myStrArr); // ["Hello", "World"]
ここでT
はプレースホルダーであり、関数が呼び出されるときに具体的な型に置き換えられます。
ブラウザで検証可能
TypeScript: JavaScript With Syntax For Types.
参考
実践 AWS CDK - TypeScript でインフラもアプリも! - Silverworks - BOOTH
クラス (class) | TypeScript入門『サバイバルTypeScript』
クラスの継承 (inheritance) | TypeScript入門『サバイバルTypeScript』
抽象クラス (abstract class) | TypeScript入門『サバイバルTypeScript』
Discussion