# TypeScript - Tương thích Loại

Xin chào các pháp sư lập trình tương lai! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào thế giới của TypeScript và khám phá khái niệm fascinating về Tương thích Loại. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình - Tôi sẽ là hướng dẫn viên thân thiện của bạn, và chúng ta sẽ cùng nhau giải quyết chủ đề này từng bước một. Vậy, hãy lấy键盘 (keyboards) ảo của bạn và cùng nhau thực hiện một số phép thuật TypeScript!

## TypeScript Kiểm tra Tương thích Loại Như Thế Nào?

Hãy tưởng tượng bạn đang cố gắng phù hợp các khối hình khác nhau vào một lỗ. Tương thích loại của TypeScript hơi giống như vậy - nó là về việc một loại có thể phù hợp vào một loại khác. Nhưng thay vì các hình dạng vật lý, chúng ta đang làm việc với các loại dữ liệu.

### Kiểu Structural

TypeScript sử dụng cái gọi là "kiểu structural". Điều này có nghĩa là nó quan tâm hơn đến hình dạng của một đối tượng而不是 tên loại chính xác. Hãy xem một ví dụ:

```typescript
interface Pet {
  name: string;
}

class Dog {
  name: string;
}

let pet: Pet;
let dog = new Dog();

pet = dog; // Điều này là okay!

Trong này vườn ma thuật, TypeScript nói, "Hey, cả PetDog đều có thuộc tính name kiểu string. Chúng trông alike với tôi, vì vậy chúng tương thích!" Điều này giống như nói một đinh vuông phù hợp vào một lỗ vuông, ngay cả khi một cái được gọi là "square" và cái khác là "block".

TypeScript - Type Compatibility

Kiểu Duck

Có một cụm từ thú vị trong lập trình: "Nếu nó đi như một con vịt và kêu như một con vịt, thì nó phải là một con vịt." Đây là essence của kiểu duck, và TypeScript chấp nhận triết lý này. Hãy xem nó trong hành động:

interface Quacker {
  quack(): void;
}

class Duck {
  quack() {
    console.log("Quack!");
  }
}

class Person {
  quack() {
    console.log("Tôi đang bắt chước một con vịt!");
  }
}

let quacker: Quacker = new Duck(); // Đương nhiên là okay
quacker = new Person(); // Điều này cũng ổn!

TypeScript không quan tâm rằng Person không được khai báo rõ ràng là Quacker. Nó chỉ quan tâm rằng Person có phương thức quack, giống như Quacker làm. Vì vậy, cả DuckPerson đều tương thích với Quacker.

Làm Thế Nào Để Sử Dụng Tương Thích Loại Hiệu Quả?

Sử dụng tương thích loại hiệu quả giống như việc giải một puzzle giỏi. Dưới đây là một số lời khuyên:

1. Hiểu Kiểm Tra Literal Object

TypeScript nghiêm ngặt hơn với các literal object. Hãy xem tại sao:

interface Point {
  x: number;
  y: number;
}

let p: Point;

// Điều này là okay
p = { x: 10, y: 20 };

// Điều này sẽ gây ra lỗi
p = { x: 10, y: 20, z: 30 };

TypeScript nói, "Whoa there! Tôi yêu cầu một Point, nhưng bạn lại đưa tôi cái gì đó extra (z). Điều đó không được phép!" Điều này giúp bắt lỗi tiềm ẩn nơi bạn có thể sử dụng một đối tượng sai cách.

2. Sử Dụng Các Thuộc Tính Tùy Chọn

Đôi khi, bạn muốn linh hoạt hơn. Đó là khi các thuộc tính tùy chọn rất hữu ích:

interface Options {
  color?: string;
  width?: number;
}

function configure(options: Options) {
  // ...
}

configure({ color: "red" }); // Okay
configure({ width: 100 }); // Okay
configure({}); // Cũng okay!

Bằng cách làm cho các thuộc tính tùy chọn (với dấu ?), bạn đang nói với TypeScript, "It's cool if these aren't always there."

Hàm và Tương Thích Loại

Hàm giống như những cây dao đa năng của lập trình - chúng rất linh hoạt. Hãy xem tương thích loại hoạt động như thế nào với chúng:

Tương Thích Tham Số

TypeScript rất khoan dung với các tham số hàm:

let x = (a: number) => 0;
let y = (b: number, s: string) => 0;

y = x; // Okay
x = y; // Lỗi

Điều này có thể看起来 counterintuitive, nhưng nó là an toàn. TypeScript nói, "Nếu bạn đang mong đợi một hàm có hai tham số, thì việc cho nó một hàm có ít hơn là okay. Tham số extra sẽ chỉ bị bỏ qua."

Tương Thích Kiểu Trả Về

Kiểu trả về cũng cần phải tương thích:

let x = () => ({name: "Alice"});
let y = () => ({name: "Alice", location: "Wonderland"});

x = y; // Okay
y = x; // Lỗi

Được phép trả về nhiều hơn dự kiến, nhưng không ít hơn. Điều này giống như khi bạn đặt hàng một pizza và nhận được thêm topping miễn phí - điều đó là okay! Nhưng nếu bạn đặt hàng một pizza với topping và chỉ nhận được vỏ, bạn sẽ thất vọng.

Lớp và Tương Thích Loại

Lớp giống như những bản vẽ cho các đối tượng, và chúng tuân theo các quy tắc tương thích alike:

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) { }
}

class Size {
  feet: number;
}

let a: Animal;
let s: Size;

a = s; // Okay
s = a; // Okay

TypeScript chỉ quan tâm đến các thành viên instance. Nó nói, "Cả hai đều có thuộc tính feet? Tốt đủ cho tôi!"

Thành Viên Private và Protected

Tuy nhiên, khi các lớp có thành viên private hoặc protected, mọi thứ trở nên nghiêm ngặt hơn:

class Animal {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
  constructor() { super("Rhino"); }
}

class Employee {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}

let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

animal = rhino; // Okay
animal = employee; // Lỗi: 'Animal' và 'Employee' không tương thích

AnimalEmployee có vẻ alike, TypeScript coi chúng khác nhau vì các thành viên private của chúng đến từ các khai báo khác nhau.

Kết Luận

Và thế là bạn đã có, các học trò lập trình của tôi! Chúng ta đã cùng nhau hành trình qua vùng đất của TypeScript's type compatibility. Nhớ rằng, TypeScript ở đây để giúp bạn viết mã tốt hơn, an toàn hơn. Nó giống như một pháp sư thân thiện đang nhìn chừng bạn, nhẹ nhàng gợi ý khi bạn sắp mắc lỗi.

Tiếp tục thực hành, tiếp tục thử nghiệm, và sớm bạn sẽ casting TypeScript spells như một pro! Hẹn gặp lại các bạn, chúc các bạn lập trình vui vẻ!

Credits: Image by storyset