TypeScriptにおけるクラスとインターフェースの高度な使用方法
TypeScriptにおけるクラスとインターフェースの違い
AngularやTypeScriptにおいて、オブジェクト指向プログラミングを理解することは重要です。特に、クラスとインターフェースは、コードを構造化し、保守性を高めるために不可欠な概念です。しかし、一見似ているように見えるこれらの2つのキーワードには、重要な違いがあります。この記事では、TypeScriptにおけるクラスとインターフェースの詳細な比較を提供し、それぞれのユースケースを明確にします。
クラスとは?
クラスは、オブジェクトの設計図のようなものです。オブジェクトの構造(プロパティ)と振る舞い(メソッド)を定義します。クラスのインスタンスを作成することで、具体的なオブジェクトを生成することができます。
インターフェースとは?
インターフェースは、オブジェクトが従うべきプロパティとメソッドの型を定義した型宣言です。インターフェース自体はオブジェクトを作成できませんが、クラスがインターフェースを実装することを保証します。
主な違い
項目 | クラス | インターフェース |
---|---|---|
目的 | オブジェクトの設計図を提供する | オブジェクトが従うべき型を定義する |
インスタンス化 | 可能 | 不可能 |
プロパティ | 具体的な値を持つ | 型のみを定義 |
メソッド | 具体的な実装を持つ | 型と名前のみを定義 |
継承 | 可能 | 複数インターフェースの継承が可能 |
抽象化 | 抽象メソッドを使用して部分的な実装を定義できる | 抽象メソッドの定義はできない |
可視性 | public、private、protectedなどのアクセス修飾子を定義できる | アクセス修飾子を定義できない |
例
// クラスの例
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// インターフェースの例
interface Shape {
draw(): void;
getArea(): number;
}
// クラスがインターフェースを実装する例
class Circle implements Shape {
radius: number;
constructor(radius: number) {
this.radius = radius;
}
draw() {
console.log("Drawing a circle...");
}
getArea(): number {
return Math.PI * this.radius * this.radius;
}
}
いつクラスを使用するか
- 抽象メソッドを使用して部分的な実装を定義したい場合
- 継承を使用してコードを再利用したい場合
- オブジェクトの状態と振る舞いをカプセル化したい場合
- オブジェクトの設計図を作成したい場合
いつインターフェースを使用するか
- 依存関係を緩やかにしたい場合
- 複数のクラスで共通の機能を定義したい場合
- 異なるソースからのオブジェクト間で一貫性を保ちたい場合
- オブジェクトが従うべき型を定義したい場合
この例では、Shape
インターフェースと、そのインターフェースを実装する2つのクラス(Circle
とRectangle
)を作成します。
// Shapeインターフェース
interface Shape {
draw(): void;
getArea(): number;
}
// Circleクラス
class Circle implements Shape {
radius: number;
constructor(radius: number) {
this.radius = radius;
}
draw(): void {
console.log("Drawing a circle...");
}
getArea(): number {
return Math.PI * this.radius * this.radius;
}
}
// Rectangleクラス
class Rectangle implements Shape {
width: number;
height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
draw(): void {
console.log("Drawing a rectangle...");
}
getArea(): number {
return this.width * this.height;
}
}
// メインプログラム
let circle = new Circle(5);
let rectangle = new Rectangle(10, 7);
console.log("Circle area:", circle.getArea());
circle.draw();
console.log("Rectangle area:", rectangle.getArea());
rectangle.draw();
商品管理システム
この例では、Product
インターフェースと、そのインターフェースを実装する2つのクラス(Book
とLaptop
)を作成します。
// Productインターフェース
interface Product {
id: number;
name: string;
price: number;
printDetails(): void;
}
// Bookクラス
class Book implements Product {
id: number;
name: string;
price: number;
author: string;
constructor(id: number, name: string, price: number, author: string) {
this.id = id;
this.name = name;
this.price = price;
this.author = author;
}
printDetails(): void {
console.log(`Book ID: ${this.id}`);
console.log(`Book Name: ${this.name}`);
console.log(`Book Price: ${this.price}`);
console.log(`Book Author: ${this.author}`);
}
}
// Laptopクラス
class Laptop implements Product {
id: number;
name: string;
price: number;
brand: string;
constructor(id: number, name: string, price: number, brand: string) {
this.id = id;
this.name = name;
this.price = price;
this.brand = brand;
}
printDetails(): void {
console.log(`Laptop ID: ${this.id}`);
console.log(`Laptop Name: ${this.name}`);
console.log(`Laptop Price: ${this.price}`);
console.log(`Laptop Brand: ${this.brand}`);
}
}
// メインプログラム
let book = new Book(1, "TypeScript Deep Dive", 35.99, "Paul Cooper");
let laptop = new Laptop(2, "MacBook Pro 14", 1999, "Apple");
book.printDetails();
laptop.printDetails();
- 抽象クラス
抽象クラスは、部分的な実装を持つクラスです。抽象クラスは、継承を使用してサブクラスによって具現化されることを意図しています。 - ミックスイン
TypeScriptでは、ミックスインを使用してクラスに機能を追加することができます。これは、インターフェースに似ていますが、実装を提供することができます。 - 型パラメーター
クラスとインターフェースは、型パラメーターを使用して汎用化することができます。これにより、さまざまな型のデータを持つクラスやインターフェースを作成することができます。
インターフェースを使用して、既存の型に新しいプロパティやメソッドを定義することができます。これは、ライブラリやフレームワークで使用されている型を拡張する場合に役立ちます。
interface Window {
log(message: string): void;
}
window.log("Hello from TypeScript!");
型エイリアスとして使用する
インターフェースは、複雑な型の定義をより簡潔にするために型エイリアスとして使用することができます。
type User = {
id: number;
name: string;
email: string;
};
let user: User = {
id: 1,
name: "John Doe",
email: "[email protected]"
};
ジェネリック型を作成する
interface Container<T> {
items: T[];
add(item: T): void;
remove(item: T): void;
}
let numbers: Container<number> = {
items: [1, 2, 3, 4, 5],
add(item: number) {
this.items.push(item);
},
remove(item: number) {
this.items = this.items.filter(x => x !== item);
}
};
let strings: Container<string> = {
items: ["Hello", "World", "!"],
add(item: string) {
this.items.push(item);
},
remove(item: string) {
this.items = this.items.filter(x => x !== item);
}
};
デコレータを使用する
クラスとインターフェースは、デコレータを使用して機能を追加することができます。デコレータは、クラスやインターフェースの構造を変更したり、メソッドの実装を修正したりするために使用することができます。
interface Loggable {
log(message: string): void;
}
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args: ${args}`);
originalMethod.apply(this, args);
};
}
class Calculator implements Loggable {
@logMethod
add(a: number, b: number): number {
return a + b;
}
@logMethod
subtract(a: number, b: number): number {
return a - b;
}
}
let calculator = new Calculator();
console.log(calculator.add(1, 2)); // Calling add with args: [1, 2]
console.log(calculator.subtract(5, 3)); // Calling subtract with args: [5, 3]
angular typescript class