# TypeScript - Kiểu Duck Typing: Hướng dẫn cho người mới bắt đầu

Xin chào các ngôi sao lập trình tương lai! Hôm nay, chúng ta sẽ cùng lặn vào thế giới kỳ diệu của TypeScript và khám phá một khái niệm有趣 như chính tên gọi của nó - Kiểu Duck Typing. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình; tôi sẽ dẫn dắt bạn từng bước trong hành trình này, giống như tôi đã làm cho hàng trăm học sinh trong những năm dạy học của mình. Vậy, chúng ta hãy cùng bắt đầu!

## 什么是 Duck Typing?

Hãy tưởng tượng bạn đang ở một ao, và bạn thấy một con chim đang bơi. Nó trông như một con vịt, kêu như một con vịt, và bơi như một con vịt. Bạn sẽ gọi nó là gì? Đúng rồi, một con vịt! Đó chính là essence của Kiểu Duck Typing trong lập trình!

Kiểu Duck Typing là khái niệm rằng, "Nếu nó đi như một con vịt và kêu như một con vịt, thì nó chắc chắn là một con vịt." Trong lập trình, điều này có nghĩa là loại hoặc lớp của một đối tượng không quan trọng bằng các phương thức nó định nghĩa hoặc các thuộc tính nó có.

Hãy để tôi chia sẻ một câu chuyện nhỏ từ những ngày dạy học của mình. Tôi từng có một học sinh rất khó hiểu về các kiểu trong lập trình. Tôi đã bảo cô ấy hãy nghĩ về hộp bút của mình. Không quan trọng nếu nó làm từ nhựa, kim loại hay vải - miễn là nó có thể chứa bút và còi của cô ấy, nó đã làm tốt công việc của một hộp bút. Đó chính là Kiểu Duck Typing trong hành động!

### Kiểu Duck Typing hoạt động như thế nào trong TypeScript

Trong TypeScript, Kiểu Duck Typing cho phép chúng ta sử dụng một đối tượng như thể nó là một kiểu nhất định, miễn là nó có tất cả các thuộc tính và phương thức cần thiết của kiểu đó. Đó như thể nói, "Nếu nó có tất cả các tính năng tôi cần, tôi không quan tâm nó gọi mình là gì!"

Hãy xem một ví dụ đơn giản:

```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); // Output: Squeak! Squeak!
makeItQuack(realDuck);   // Output: Quack! Quack!

Trong ví dụ này, chúng ta có một Quackable interface yêu cầu một phương thức quack. Phương thức makeItQuack chấp nhận bất kỳ đối tượng nào phù hợp với interface này. Cả rubberDuckrealDuck đều có phương thức quack, vì vậy chúng đều có thể sử dụng với makeItQuack, mặc dù realDuck có thêm phương thức swim.

TypeScript - Duck-Typing

Lợi ích của Kiểu Duck Typing

Bây giờ chúng ta đã hiểu Kiểu Duck Typing là gì, hãy cùng khám phá tại sao nó lại tuyệt vời như vậy! Dưới đây là một số lợi ích quan trọng:

1. Độ linh hoạt

Kiểu Duck Typing cho phép mã linh hoạt hơn. Bạn không cần phải tạo một cấu trúc cứng nhắc các lớp để sử dụng các đối tượng theo cùng một cách. Chỉ cần một đối tượng có các phương thức hoặc thuộc tính cần thiết, nó có thể được sử dụng thay thế cho nhau.

2. Tái sử dụng mã

Với Kiểu Duck Typing, bạn có thể viết các hàm làm việc với một phạm vi rộng các đối tượng, miễn là chúng có các thuộc tính hoặc phương thức cần thiết. Điều này thúc đẩy việc tái sử dụng mã và có thể làm cho cơ sở mã của bạn hiệu quả hơn.

3. Đơn giản hóa việc kiểm thử

Kiểu Duck Typing làm cho việc tạo các đối tượng giả cho kiểm thử trở nên dễ dàng hơn. Bạn chỉ cần triển khai các phương thức thực sự được sử dụng trong bài kiểm thử, thay vì tạo một lớp đầy đủ.

4. Thiết kế trực quan

Kiểu Duck Typing thường dẫn đến các thiết kế API trực quan hơn. Bạn tập trung vào những gì một đối tượng có thể làm, thay vì nó được gọi là gì.

Ví dụ về Kiểu Duck Typing trong TypeScript

Hãy cùng xem thêm một số ví dụ để củng cố hiểu biết của chúng ta về Kiểu Duck Typing trong TypeScript.

Ví dụ 1: The Shape-Shifter

interface Drawable {
    draw(): void;
}

class Circle {
    draw() {
        console.log("Drawing a circle");
    }
}

class Square {
    draw() {
        console.log("Drawing a square");
    }
}

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

drawShape(new Circle()); // Output: Drawing a circle
drawShape(new Square()); // Output: Drawing a square

Trong ví dụ này, CircleSquare là các lớp khác nhau, nhưng cả hai đều có phương thức draw. Hàm drawShape không quan tâm đến lớp cụ thể của đối tượng nó nhận; nó chỉ quan tâm đến việc đối tượng có phương thức draw hay không.

Ví dụ 2: The Noise Makers

interface NoiseMaker {
    makeNoise(): string;
}

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

let cat = {
    makeNoise: () => "Meow!",
    purr: () => "Purr..."
};

let car = {
    makeNoise: () => "Vroom!",
    drive: () => console.log("Driving...")
};

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

makeNoise(dog); // Output: Woof!
makeNoise(cat); // Output: Meow!
makeNoise(car); // Output: Vroom!

Tại đây, chúng ta có các đối tượng khác nhau - một con chó, một con mèo, và thậm chí một chiếc xe! Cả ba đều có phương thức makeNoise, vì vậy chúng đều có thể sử dụng với hàm makeNoise, mặc dù chúng rất khác nhau.

Ví dụ 3: The Fly-Swimmer

interface Flyable {
    fly(): void;
}

interface Swimmable {
    swim(): void;
}

class Duck implements Flyable, Swimmable {
    fly() {
        console.log("Duck is flying");
    }
    swim() {
        console.log("Duck is swimming");
    }
}

class Airplane implements Flyable {
    fly() {
        console.log("Airplane is flying");
    }
}

class Fish implements Swimmable {
    swim() {
        console.log("Fish is swimming");
    }
}

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);     // Output: Duck is flying
makeFly(airplane); // Output: Airplane is flying
makeSwim(duck);    // Output: Duck is swimming
makeSwim(fish);    // Output: Fish is swimming

Trong ví dụ này, chúng ta có các lớp khác nhau triển khai các interface khác nhau. Lớp Duck triển khai cả hai interface FlyableSwimmable, vì vậy nó có thể sử dụng với cả hai hàm makeFlymakeSwim.

Kết luận

Và thế là chúng ta đã cùng nhau đi qua các khái niệm cơ bản của Kiểu Duck Typing trong TypeScript. Nhớ rằng, điểm chính là: trong Kiểu Duck Typing, chúng ta quan tâm hơn đến những gì một đối tượng có thể làm (các phương thức và thuộc tính của nó) hơn là nó là gì (kiểu hoặc lớp của nó).

Kiểu Duck Typing cho phép chúng ta viết mã linh hoạt, tái sử dụng và trực quan hơn. Nó là một khái niệm mạnh mẽ có thể làm cho mã TypeScript của bạn trở nên tinh tế và hiệu quả hơn.

Trong hành trình lập trình của bạn, hãy để ý các cơ hội để áp dụng Kiểu Duck Typing. Và nhớ rằng, nếu nó trông như một con vịt, bơi như một con vịt, và kêu như một con vịt, thì nó có lẽ là một con vịt... hoặc chí ít, nó có thể được đối xử như một con vịt trong mã của bạn!

Chúc các bạn lập trình vui vẻ, và mã của bạn luôn hoạt động hoàn hảo!

Credits: Image by storyset