Trong TypeScript, classesinterfaces đều đóng vai trò quan trọng trong việc định nghĩa cấu trúc và mô hình cho ứng dụng, nhưng chúng có những mục đích và chức năng khác nhau. Dưới đây là các điểm khác biệt chính giữa classesinterfaces trong TypeScript:

1. Chức năng cơ bản

  • Classes: Là một khái niệm của lập trình hướng đối tượng, giúp tạo ra các thực thể (objects) và cung cấp cả dữ liệu (thuộc tính) lẫn hành vi (phương thức). Các class có thể chứa logic thực thi và các phương thức khởi tạo (constructor) để tạo ra các đối tượng.
  • Interfaces: Chỉ được sử dụng để mô tả hình dạng của đối tượng hoặc các kiểu dữ liệu khác. Interface không chứa bất kỳ logic nào và chỉ có nhiệm vụ định nghĩa các thuộc tính và phương thức mà một lớp hoặc một đối tượng phải tuân thủ.

Ví dụ về Class:

class Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    speak(): void {
        console.log(`${this.name} makes a sound.`);
    }
}

const dog = new Animal("Dog");
dog.speak();  // "Dog makes a sound."

Ví dụ về Interface:

interface Animal {
    name: string;
    speak(): void;
}

let cat: Animal = {
    name: "Cat",
    speak: () => console.log("Meow!")
};

cat.speak();  // "Meow!"

2. Tính thực thi (Implementation)

  • Classes: Chứa cả dữ liệu và logic xử lý. Một class có thể thực thi các phương thức của chính nó và cho phép tạo các thực thể (instances) từ class đó.
  • Interfaces: Chỉ định nghĩa các thuộc tính và phương thức, không chứa logic thực thi. Các lớp thực hiện (implements) interface phải cung cấp logic cho các phương thức đã được định nghĩa trong interface.

Ví dụ:

interface Vehicle {
    speed: number;
    move(): void;
}

class Car implements Vehicle {
    speed: number;

    constructor(speed: number) {
        this.speed = speed;
    }

    move(): void {
        console.log(`The car is moving at ${this.speed} km/h.`);
    }
}

const car = new Car(100);
car.move();  // "The car is moving at 100 km/h."

3. Kế thừa và Đa kế thừa

  • Classes: Hỗ trợ tính kế thừa (inheritance), nhưng một class chỉ có thể kế thừa từ một class khác. Điều này đảm bảo cấu trúc phân cấp rõ ràng trong lập trình hướng đối tượng.
  • Interfaces: Hỗ trợ đa kế thừa (multiple inheritance). Một interface có thể mở rộng nhiều interface khác, và một class có thể thực hiện nhiều interface. Điều này cho phép bạn linh hoạt trong việc xây dựng các hệ thống kiểu dữ liệu phức tạp.

Ví dụ:

interface Animal {
    eat(): void;
}

interface Mammal {
    walk(): void;
}

class Human implements Animal, Mammal {
    eat(): void {
        console.log("Eating");
    }

    walk(): void {
        console.log("Walking");
    }
}

4. Visibility Modifiers (Bộ điều chỉnh truy cập)

  • Classes: Hỗ trợ các bộ điều chỉnh truy cập như public, private, và protected, giúp kiểm soát mức độ truy cập đến các thành viên của class.
  • Interfaces: Không hỗ trợ các bộ điều chỉnh truy cập. Tất cả các thuộc tính và phương thức trong interface đều mặc định là công khai (public).

Ví dụ:

class Person {
    private name: string;  // Chỉ có thể truy cập từ bên trong class Person

    constructor(name: string) {
        this.name = name;
    }

    getName(): string {
        return this.name;
    }
}

const john = new Person("John");
// console.log(john.name); // Lỗi vì name là private
console.log(john.getName());  // Hợp lệ

5. Tạo đối tượng

  • Classes: Có thể được khởi tạo để tạo ra các thực thể (instance) thông qua từ khóa new. Điều này cho phép tạo nhiều đối tượng dựa trên một class.
  • Interfaces: Không thể khởi tạo. Interface chỉ có nhiệm vụ định nghĩa kiểu, không tạo ra các đối tượng cụ thể.

Ví dụ về tạo đối tượng với class:

class Dog {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

const myDog = new Dog("Buddy");  // Tạo một thực thể của class Dog

Ví dụ interface không thể tạo đối tượng:

interface Dog {
    name: string;
}

// const myDog = new Dog(); // Lỗi: Interface không thể khởi tạo

6. Các trường hợp sử dụng (Use Cases)

  • Classes: Sử dụng khi bạn cần mô hình hóa một thực thể với cả dữ liệu và hành vi. Classes thích hợp cho các trường hợp đòi hỏi xử lý logic phức tạp, kế thừa và tạo nhiều đối tượng.
  • Interfaces: Sử dụng khi bạn chỉ cần mô tả cấu trúc của đối tượng mà không cần phải cài đặt logic. Interface thường được dùng để đảm bảo rằng một lớp hoặc đối tượng tuân theo một cấu trúc cụ thể.

Kết luận

Tóm lại, classes trong TypeScript giúp định nghĩa cả cấu trúc và logic của đối tượng, hỗ trợ việc kế thừa, đóng gói và các tính năng lập trình hướng đối tượng khác. Interfaces, ngược lại, chỉ mô tả cấu trúc dữ liệu và không chứa logic thực thi. Chúng ta sử dụng interface khi muốn đảm bảo rằng các đối tượng hoặc lớp có hình dạng và hành vi nhất định mà không yêu cầu việc triển khai cụ thể.