# TypeScript -.Generic Constraints: Unleashing the Power of Flexible Types

Xin chào các pháp sư TypeScript tương lai! Hôm nay, chúng ta sẽ bắt đầu một hành trình đầy thú vị vào thế giới của Generic Constraints. Đừ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ẽ giải quyết chủ đề này từng bước một. Cuối cùng của bài hướng dẫn này, bạn sẽ约束 generics như một chuyên gia!

## What Are Generic Constraints? (Những gì là Generic Constraints?)

Trước khi chúng ta đi vào chi tiết, hãy bắt đầu với một ví dụ đơn giản. Hãy tưởng tượng bạn có một hộp ma thuật có thể chứa bất kỳ loại vật phẩm nào. Đó chính là generic trong TypeScript - một容器 linh hoạt cho các loại khác nhau. Bây giờ, nếu chúng ta muốn đặt một số quy tắc về những gì có thể vào trong hộp đó? Đó chính là vai trò của generic constraints!

Generic constraints cho phép chúng ta giới hạn các loại có thể được sử dụng với generics. Nó giống như đặt một nhãn trên hộp ma thuật của chúng ta rằng, "Chỉ允许拥有 'length' 属性 đối tượng vào!"

## Problem Examples: Why Do We Need Generic Constraints? (Ví dụ về vấn đề: Tại sao chúng ta cần Generic Constraints?)

Hãy xem một vài kịch bản nơi generic constraints có thể cứu nguy:

### Example 1: The Mysterious Length Property (Ví dụ 1: Tính chất Length bí ẩn)

```typescript
function getLength<T>(item: T): number {
    return item.length; // Error: Property 'length' does not exist on type 'T'
}

Oops! TypeScript đang cho chúng ta một lỗi. Tại sao? Bởi vì không phải tất cả các loại đều có thuộc tính length. Nếu chúng ta truyền một số vào hàm này? Số không có độ dài!

TypeScript - Generic Constraints

Example 2: The Confusing Comparison (Ví dụ 2: So sánh rối loạn)

function compareValues<T>(value1: T, value2: T): boolean {
    return value1 > value2; // Error: Operator '>' cannot be applied to types 'T' and 'T'
}

Lại một lỗi khác! TypeScript không biết nếu T có thể được so sánh bằng >. Nếu chúng ta truyền chuỗi? Hay các đối tượng phức tạp?

Những ví dụ này cho thấy tại sao chúng ta cần generic constraints. Chúng giúp chúng ta viết mã chính xác hơn và không có lỗi.

How Generic Constraints Work in TypeScript (Generic Constraints hoạt động như thế nào trong TypeScript)

Bây giờ, hãy xem cách chúng ta có thể sử dụng generic constraints để giải quyết vấn đề của mình:

The Magical extends Keyword (Từ khóa extends ma thuật)

Để thêm một constraint, chúng ta sử dụng từ khóa extends. Nó giống như nói với TypeScript, "Hey, loại này phải có ít nhất những thuộc tính hoặc khả năng này!"

Hãy sửa hàm getLength của chúng ta:

interface Lengthwise {
    length: number;
}

function getLength<T extends Lengthwise>(item: T): number {
    return item.length; // No more error!
}

Bây giờ, hãy phân tích này:

  1. Chúng ta định nghĩa một interface Lengthwise có thuộc tính length.
  2. Chúng ta sử dụng <T extends Lengthwise> để nói "T phải có ít nhất những gì Lengthwise có".
  3. Bây giờ TypeScript biết rằng bất kể T là gì, nó sẽ có thuộc tính length!

Hãy thử nó ra:

console.log(getLength("Hello")); // Works! Strings have length
console.log(getLength([1, 2, 3])); // Works! Arrays have length
console.log(getLength(123)); // Error! Numbers don't have length

Có phải đó là tuyệt vời không? Chúng ta đã thành công trong việc约束 generics!

Using Type Parameters in Generic Constraints (Sử dụng tham số loại trong Generic Constraints)

Đôi khi, chúng ta muốn约束 một tham số loại dựa trên một tham số loại khác. Nó giống như nói, "Hộp này chỉ có thể chứa các vật phẩm tương thích với những gì đã có trong nó."

Hãy xem một ví dụ:

function copyProperties<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = source[id];
    }
    return target;
}

Điều gì đang xảy ra ở đây?

  1. Chúng ta có hai tham số loại: TU.
  2. T extends U có nghĩa là T phải ít nhất là tất cả những gì U có, nhưng có thể có thêm.
  3. Điều này cho phép chúng ta sao chép các thuộc tính từ source đến target, biết rằng target sẽ có tất cả các thuộc tính của source.

Hãy xem nó trong hành động:

interface Person {
    name: string;
}

interface Employee extends Person {
    employeeId: number;
}

let person: Person = { name: "Alice" };
let employee: Employee = { name: "Bob", employeeId: 123 };

copyProperties(employee, person); // Works!
copyProperties(person, employee); // Error! Person doesn't have employeeId

Practical Applications and Best Practices (Ứng dụng thực tế và các nguyên tắc tốt nhất)

Bây giờ chúng ta đã hiểu cách generic constraints hoạt động, hãy xem một số ứng dụng thực tế và các nguyên tắc tốt nhất:

  1. Constraint to Object Types: Thường xuyên, bạn sẽ muốn đảm bảo bạn đang làm việc với các đối tượng:

    function cloneObject<T extends object>(obj: T): T {
        return { ...obj };
    }
  2. Constraint to Function Types: Bạn có thể đảm bảo một loại là callable:

    function invokeFunction<T extends Function>(func: T): void {
        func();
    }
  3. Constraint to Specific Properties: Đảm bảo các đối tượng có các thuộc tính cụ thể:

    function getFullName<T extends { firstName: string; lastName: string }>(obj: T): string {
        return `${obj.firstName} ${obj.lastName}`;
    }
  4. Multiple Constraints: Bạn có thể áp dụng nhiều constraint sử dụng toán tử &:

    function processData<T extends number & { toFixed: Function }>(data: T): string {
        return data.toFixed(2);
    }

Dưới đây là bảng tóm tắt các phương pháp này:

Method Description Example
Object Constraint Đảm bảo loại là một đối tượng <T extends object>
Function Constraint Đảm bảo loại là callable <T extends Function>
Property Constraint Đảm bảo loại có các thuộc tính cụ thể <T extends { prop: Type }>
Multiple Constraints Kết hợp nhiều constraint <T extends TypeA & TypeB>

Conclusion: Embracing the Power of Constraints (Kết luận: Chấp nhận sức mạnh của Constraints)

Chúc mừng! Bạn vừa mở khóa một công cụ mạnh mẽ trong bộ công cụ TypeScript của mình. Generic constraints cho phép chúng ta viết mã linh hoạt nhưng vẫn an toàn về loại, mang lại cho chúng ta sự tốt nhất của cả hai thế giới.

Nhớ rằng, chìa khóa để thành thạo generic constraints là thực hành. Hãy thử refactor một số mã hiện có của bạn để sử dụng generics và constraints. Bạn sẽ ngạc nhiên khi thấy mã của bạn trở nên sạch sẽ và mạnh mẽ hơn!

Khi chúng ta kết thúc, đây là một chút hài hước lập trình cho bạn: Tại sao nhà phát triển TypeScript phá sản? Bởi vì anh ấy sử dụng quá nhiều generic constraints và không thể chấp nhận bất kỳ loại thanh toán nào! ?

Tiếp tục lập trình, tiếp tục học hỏi, và quan trọng nhất, hãy vui vẻ với TypeScript!

Credits: Image by storyset