1implements の最小例
interface が定めたメソッドをクラスが備えていることを implements で宣言する、最小の形です。約束したメソッドを書き忘れると、使う前のクラス定義の時点でエラーになるのが要点です。
interface Speaker {
speak(): string;
}
// Speaker の約束(speak メソッド)を守ると宣言する
class RobotSpeaker implements Speaker {
speak(): string {
return "ピピッ、こんにちは";
}
}
const robot = new RobotSpeaker();
console.log(robot.speak());
// speak を書き忘れると、クラスの定義時点でエラーになる
21つの interface を複数クラスで
同じ interface を implements したクラスは、実装が違っても「同じ操作ができる仲間」として扱えます。Animal[] の配列にまとめて同じメソッドを呼ぶ、ポリモーフィズムの入り口となる例です。
interface Animal {
cry(): string;
}
class Dog implements Animal {
cry(): string {
return "ワン!";
}
}
class Cat implements Animal {
cry(): string {
return "ニャー";
}
}
// どちらも Animal として同じ配列に入れられる
const animals: Animal[] = [new Dog(), new Cat()];
for (const animal of animals) {
console.log(animal.cry());
}
3プロパティの約束も検査される
interface にはメソッドだけでなくプロパティも書け、implements したクラスは両方を備える必要があります。「データと操作のセット」を型として約束する形で、約束のどれが欠けても定義時点でエラーになります。
interface Vehicle {
wheels: number;
describe(): string;
}
// メソッドだけでなくプロパティの約束も検査される
class Motorcycle implements Vehicle {
wheels: number = 2;
describe(): string {
return "車輪が " + this.wheels + " 個の乗り物です";
}
}
const bike = new Motorcycle();
console.log(bike.wheels);
console.log(bike.describe());
// wheels を書き忘れると implements の検査でエラーになる
4interface 型の引数で実装を差し替える
使う側の関数は Logger という interface だけに依存し、どの実装クラスを渡すかは呼び出し側が決める例です。テスト時だけ実装を差し替えるなど、実務の「依存性の注入」の最小形がこのパターンです。
interface Logger {
log(message: string): void;
}
class PlainLogger implements Logger {
log(message: string): void {
console.log(message);
}
}
class PrefixLogger implements Logger {
log(message: string): void {
console.log("[app] " + message);
}
}
// 使う側は Logger という約束だけに依存する
function runJob(logger: Logger): void {
logger.log("ジョブを実行しました");
}
runJob(new PlainLogger());
runJob(new PrefixLogger()); // 実装を自由に差し替えられる
5支払い方法を差し替えられる checkout
支払いという共通の操作を interface に定め、カード払い・銀行振込をそれぞれのクラスで実装する実務的な例です。新しい支払い方法が増えても checkout 関数は一切変更せず、クラスを追加するだけで対応できます。
interface PaymentMethod {
pay(amount: number): string;
}
class CreditCard implements PaymentMethod {
pay(amount: number): string {
return "カードで " + amount + " 円を決済しました";
}
}
class BankTransfer implements PaymentMethod {
pay(amount: number): string {
return "振込先を発行しました(" + amount + " 円)";
}
}
// 支払い方法が増えても checkout は変更不要
function checkout(method: PaymentMethod, amount: number): void {
console.log(method.pay(amount));
}
checkout(new CreditCard(), 3200);
checkout(new BankTransfer(), 3200);