TypeScript - ダックタイピング:入門ガイド

こんにちは、将来のプログラミングスーパースターたち!今日は、TypeScriptの素晴らしい世界に飛び込み、楽しさそのもののコンセプトを探求します。ダックタイピングです。プログラミングの初心者でも心配しないでください。私は、これまでに数多くの学生を指導してきた経験を活かして、ステップバイステップで案内します。では、さっそく始めましょう!

TypeScript - Duck-Typing

ダックタイピングとは?

想象していただき、池のそばにいる鳥を見てください。その鳥は鸭子のようだし、鸭子のように鳴き、鸭子のように泳ぐ。それを何と呼ぶでしょう?鸭子ですね、間違いないですよね。これがプログラミングにおけるダックタイピングの本質です!

ダックタイピングとは、「鸭子のように歩き、鸭子のように鳴き、鸭子のように泳ぐものは鸭子である」というコンセプトです。プログラミングの言葉では、オブジェクトの型やクラスよりも、そのオブジェクトが定義するメソッドやプロパティのほうが重要です。

私の教え子の話を少しご紹介しましょう。ある生徒がプログラミングの型を理解するのに苦労していました。私は彼女に、彼女のペンケースについて考えさせました。ペンケースがプラスチック製であれ、金属製であれ、布製であれ、ペンや pencils を保持できるなら、それはペンケースとしての役割を果たしているんです。これがダックタイピングの実際の应用です!

TypeScriptにおけるダックタイピングの動作

TypeScriptでは、ダックタイピングにより、オブジェクトが特定の型として使用できるようにします。そのオブジェクトがその型が必要とするすべてのプロパティとメソッドを持っている限り、です。これは、「必要な機能がすべて揃っているなら、それが何であるかは気にしない」と言うことです!

簡単な例を見てみましょう:

interface Quackable {
quack(): void;
}

function makeItQuack(duck: Quackable) {
duck.quack();
}

let rubberDuck = {
quack: () => console.log("Squeak! Squeak!")
};

let realDuck = {
quack: () => console.log("Quack! Quack!"),
swim: () => console.log("Splash! Splash!")
};

makeItQuack(rubberDuck); // 出力: Squeak! Squeak!
makeItQuack(realDuck);   // 出力: Quack! Quack!

この例では、Quackableインターフェースがquackメソッドを要求しています。私たちのmakeItQuack関数は、このインターフェースに一致する任意のオブジェクトを受け取ります。rubberDuckrealDuckの両方がquackメソッドを持っているため、どちらもmakeItQuackを使用できます。realDuckには追加のswimメソッドがありますが、問題ありません。

ダックタイピングの利点

ダックタイピングが何であるかを理解したところで、なぜそれが素晴らしいかを探ってみましょう。以下にいくつかの主要な利点を挙げます:

1. 柔軟性

ダックタイピングにより、より柔軟なコードを書くことができます。オブジェクトを同様に使用するために厳格なクラスの階層を作成する必要はありません。オブジェクトが必要なメソッドやプロパティを持っていれば、それらを互换的に使用できます。

2. コードの再利用性

ダックタイピングにより、必要なプロパティやメソッドを持つ広範なオブジェクトで動作する関数を書くことができます。これにより、コードの再利用が促進され、コードベースがより効率的になります。

3. より簡単なテスト

ダックタイピングにより、テスト用のモックオブジェクトを簡単に作成できます。テストで実際に使用されるメソッドを実装するだけで済みます。クラスの完全な実装を作成する必要はありません。

4. 直感的な設計

ダックタイピングはしばしばより直感的なAPI設計につながります。オブジェクトが何であるかではなく、何ができるかに焦点を当てます。

TypeScriptにおけるダックタイピングの例

ダックタイピングの理解を深めるために、TypeScriptにおけるいくつかの例を見てみましょう。

例 1: 形の変化者

interface Drawable {
draw(): void;
}

class Circle {
draw() {
console.log("円を描く");
}
}

class Square {
draw() {
console.log("四角形を描く");
}
}

function drawShape(shape: Drawable) {
shape.draw();
}

drawShape(new Circle()); // 出力: 円を描く
drawShape(new Square()); // 出力: 四角形を描く

この例では、CircleSquareは異なるクラスですが、どちらもdrawメソッドを持っています。drawShape関数は、オブジェクトの特定のクラスを気にせず、drawメソッドを持っているかどうかだけを気にします。

例 2: 声を発する者たち

interface NoiseMaker {
makeNoise(): string;
}

let dog = {
makeNoise: () => "ワンワン!"
};

let cat = {
makeNoise: () => "メイメイ!",
purr: () => "パurr..."
};

let car = {
makeNoise: () => "ブroom!",
drive: () => console.log("運転...")
};

function makeNoise(thing: NoiseMaker) {
console.log(thing.makeNoise());
}

makeNoise(dog); // 出力: ワンワン!
makeNoise(cat); // 出力: メイメイ!
makeNoise(car); // 出力: ブroom!

ここでは、異なるオブジェクト - 犬、猫、そして車があります。それらはすべてmakeNoiseメソッドを持っているため、makeNoise関数で使用できます。それらは非常に異なるものですが、問題ありません。

例 3: 飛行する泳ぐ者

interface Flyable {
fly(): void;
}

interface Swimmable {
swim(): void;
}

class Duck implements Flyable, Swimmable {
fly() {
console.log("鸭子が飛んでいます");
}
swim() {
console.log("鸭子が泳いでいます");
}
}

class Airplane implements Flyable {
fly() {
console.log("飛行機が飛んでいます");
}
}

class Fish implements Swimmable {
swim() {
console.log("魚が泳いでいます");
}
}

function makeFly(flyer: Flyable) {
flyer.fly();
}

function makeSwim(swimmer: Swimmable) {
swimmer.swim();
}

let duck = new Duck();
let airplane = new Airplane();
let fish = new Fish();

makeFly(duck);     // 出力: 鸭子が飛んでいます
makeFly(airplane); // 出力: 飛行機が飛んでいます
makeSwim(duck);    // 出力: 鸭子が泳いでいます
makeSwim(fish);    // 出力: 魚が泳いでいます

この例では、異なるクラスが異なるインターフェースを実装しています。DuckクラスはFlyableおよびSwimmableインターフェースの両方を実装しており、したがって、makeFlyおよびmakeSwim関数の両方で使用できます。

結論

そしてここまで、鸭子タイピングの基本を一緒に学びました。鍵を握るのはこのことです:鸭子タイピングでは、オブジェクトが何であるか(その型やクラス)よりも、オブジェクトが何をできるか(そのメソッドやプロパティ)のほうが重要です。

鸭子タイピングにより、より柔軟で再利用可能で直感的なコードを書くことができます。TypeScriptのコードをより優雅で効率的にする強力なコンセプトです。

プログラミングの旅を続ける中で、鸭子タイピングを適用できる機会を探してみてください。そして、もしそれが鸭子のように見える、鸭子のように泳ぐ、鸭子のように鳴くものがあったら、それはおそらく鸭子として扱える...少なくとも、あなたのコードでは鸭子として扱えるでしょう!

幸せなプログラミングを、そしてあなたのコードが常に完美に動作することを祈っています!

Credits: Image by storyset