Structural typing là một khái niệm quan trọng trong TypeScript, cho phép xác định kiểu dữ liệu dựa trên cấu trúc của nó thay vì tên của nó. Điều này có nghĩa là hai kiểu dữ liệu được coi là tương đương nếu chúng có cùng cấu trúc, cho phép TypeScript kiểm tra tính tương thích của các loại mà không cần dựa vào tên của chúng. Trong bài viết này, chúng ta sẽ khám phá cấu trúc của structural typing, cách hoạt động và các ví dụ minh họa.

Cách Hoạt Động của Structural Typing

Trong TypeScript, các kiểu dữ liệu được xác định không chỉ bởi tên của chúng mà còn bởi các thuộc tính và phương thức mà chúng có. Nếu hai kiểu có cùng một cấu trúc (tức là, các thuộc tính và kiểu dữ liệu của chúng phù hợp), TypeScript sẽ coi chúng là tương đương.

Ví dụ Cơ Bản

interface Person {
    name: string;
    age: number;
}

function greet(person: Person) {
    console.log(`Hello, ${person.name}. You are ${person.age} years old.`);
}

const user = { name: "Alice", age: 25, location: "Wonderland" };
greet(user); // Kết quả: Hello, Alice. You are 25 years old.

Trong ví dụ trên, hàm greet nhận vào một tham số có kiểu là Person. Mặc dù user có thêm thuộc tính location, TypeScript vẫn cho phép truyền đối tượng này vào hàm greet vì nó có đủ các thuộc tính cần thiết (nameage).

Tính Tương Thích của Các Kiểu

Tính Tương Thích Dựa Trên Cấu Trúc

Structural typing cho phép tính tương thích giữa các kiểu mà không cần phải khai báo chúng như là con của một lớp hay giao diện. Điều này giúp cho việc sử dụng các kiểu dữ liệu trở nên linh hoạt hơn.

Ví dụ Về Tính Tương Thích

interface Vehicle {
    wheels: number;
    drive(): void;
}

const car = {
    wheels: 4,
    drive() {
        console.log("Driving a car!");
    }
};

function useVehicle(vehicle: Vehicle) {
    console.log(`This vehicle has ${vehicle.wheels} wheels.`);
    vehicle.drive();
}

useVehicle(car); // Kết quả: This vehicle has 4 wheels. Driving a car!

Trong ví dụ này, đối tượng car có cùng cấu trúc với Vehicle, vì vậy nó có thể được truyền vào hàm useVehicle mà không gặp lỗi.

Tính Kín Đáo và Kế Thừa

Tính Kín Đáo

Structural typing hỗ trợ tính kín đáo, nghĩa là bạn có thể xác định các kiểu mà không cần phải định nghĩa chúng trước đó.

function logCoordinates(point: { x: number; y: number }) {
    console.log(`X: ${point.x}, Y: ${point.y}`);
}

const coordinates = { x: 10, y: 20 };
logCoordinates(coordinates); // Kết quả: X: 10, Y: 20

Trong trường hợp này, bạn không cần phải tạo một giao diện cho điểm, chỉ cần định nghĩa một kiểu trong tham số của hàm là đủ.

Kế Thừa

Cấu trúc kiểu còn cho phép bạn kế thừa từ các kiểu khác mà không cần phải khai báo lại toàn bộ.

interface Employee {
    id: number;
    name: string;
}

interface Manager extends Employee {
    department: string;
}

const manager: Manager = {
    id: 1,
    name: "Bob",
    department: "Sales"
};

function showManager(manager: Manager) {
    console.log(`${manager.name} is the manager of ${manager.department}`);
}

showManager(manager); // Kết quả: Bob is the manager of Sales

Trong ví dụ này, Manager kế thừa từ Employee, và bạn có thể sử dụng Manager như là một kiểu cho hàm showManager.

Kết Luận

Structural typing trong TypeScript mang đến một cách tiếp cận linh hoạt để xác định và kiểm tra các kiểu dữ liệu. Thay vì dựa vào tên lớp hoặc giao diện, TypeScript cho phép bạn sử dụng cấu trúc của các kiểu để xác định tính tương thích. Điều này không chỉ làm cho mã nguồn trở nên rõ ràng và dễ duy trì hơn mà còn giúp bạn linh hoạt hơn trong việc làm việc với các kiểu dữ liệu khác nhau.