TypeScript Generics với Ví dụ

Generic TypeScript là một tính năng mạnh mẽ cho phép bạn tạo các thành phần có thể tái sử dụng và an toàn về kiểu. Generic cung cấp một cách để tạo các lớp, hàm và giao diện hoạt động với nhiều kiểu khác nhau trong khi vẫn duy trì tính an toàn về kiểu mạnh. Bài viết này sẽ giới thiệu cho bạn về generic và trình bày cách sử dụng chúng với các ví dụ thực tế.

Hiểu về thuốc Generic

Generic cho phép bạn định nghĩa một thành phần với một trình giữ chỗ cho loại mà nó hoạt động. Thay vì chỉ định một loại cụ thể, bạn sử dụng một tham số loại chung có thể được thay thế bằng bất kỳ loại nào khi thành phần được sử dụng.

Cú pháp cơ bản

Cú pháp cơ bản để định nghĩa một kiểu chung là sử dụng dấu ngoặc nhọn <> với tên tham số kiểu. Sau đây là một ví dụ đơn giản:

function identity(value: T): T {
  return value;
}

const stringIdentity = identity("Hello"); // string
const numberIdentity = identity(123); // number

Trong ví dụ này, identity là một hàm chung lấy tham số value có kiểu T và trả về giá trị có cùng kiểu. Tham số kiểu T được thay thế bằng kiểu thực tế khi hàm được gọi.

Generics với các lớp

Generic cũng có thể được sử dụng với các lớp để tạo ra các cấu trúc dữ liệu linh hoạt và có thể tái sử dụng. Sau đây là một ví dụ về một lớp generic:

class Box {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }
}

const stringBox = new Box("TypeScript");
console.log(stringBox.getValue()); // Output: TypeScript

const numberBox = new Box(42);
console.log(numberBox.getValue()); // Output: 42

Trong ví dụ này, lớp Box được định nghĩa với tham số kiểu chung T. Lớp có thuộc tính riêng value kiểu T và phương thức getValue trả về giá trị kiểu T.

Generics với Giao diện

Generic có thể được sử dụng với giao diện để tạo ra giao diện linh hoạt và an toàn về kiểu. Sau đây là một ví dụ:

interface Pair<T, U> {
  first: T;
  second: U;
}

const pair: Pair<string, number> = {
  first: "Age",
  second: 30
};

console.log(pair.first); // Output: Age
console.log(pair.second); // Output: 30

Trong ví dụ này, giao diện Pair được định nghĩa với hai tham số kiểu chung TU. Giao diện biểu diễn một cặp giá trị với các kiểu tương ứng là TU.

Generic trong các hàm

Generic có thể được sử dụng trong các hàm để xử lý nhiều kiểu trong khi vẫn duy trì tính an toàn của kiểu. Sau đây là ví dụ về một hàm generic hoạt động với mảng:

function reverseArray(items: T[]): T[] {
  return items.reverse();
}

const reversedStringArray = reverseArray(["one", "two", "three"]);
console.log(reversedStringArray); // Output: ["three", "two", "one"]

const reversedNumberArray = reverseArray([1, 2, 3]);
console.log(reversedNumberArray); // Output: [3, 2, 1]

Trong ví dụ này, hàm reverseArray lấy một mảng có kiểu T và trả về một mảng đảo ngược có cùng kiểu. Tham số kiểu T đảm bảo rằng hàm hoạt động với các mảng có bất kỳ kiểu nào trong khi vẫn duy trì tính an toàn của kiểu.

Những hạn chế về Generics

Đôi khi bạn có thể cần áp đặt các ràng buộc cho tham số kiểu chung để đảm bảo nó có các thuộc tính nhất định. Điều này được thực hiện bằng cách sử dụng các ràng buộc:

function logLength(item: T): void {
  console.log(item.length);
}

logLength("Hello, TypeScript"); // Output: 16
logLength([1, 2, 3]); // Output: 3
// logLength(123); // Error: number does not have a length property

Trong ví dụ này, hàm logLength bị giới hạn ở các kiểu có thuộc tính length. Điều này cho phép hàm chấp nhận chuỗi và mảng nhưng không chấp nhận số hoặc các kiểu khác không có thuộc tính length.

Phần kết luận

Generic trong TypeScript cung cấp một cách mạnh mẽ để tạo các thành phần linh hoạt và có thể tái sử dụng trong khi vẫn duy trì tính an toàn của kiểu mạnh. Bằng cách hiểu và sử dụng generic, bạn có thể viết mã chung và dễ thích ứng hơn, cải thiện chất lượng và khả năng bảo trì chung của các ứng dụng TypeScript của bạn.

Hãy thử nghiệm với kiểu chung trong các dự án của bạn để thấy được lợi ích của chúng và nâng cao kỹ năng lập trình TypeScript của bạn.