Generics là một tính năng mạnh mẽ trong TypeScript cho phép bạn tạo ra các hàm, lớp và giao diện có thể làm việc với nhiều loại dữ liệu khác nhau mà không cần phải xác định trước loại dữ liệu cụ thể. Điều này giúp mã trở nên linh hoạt và tái sử dụng hơn, đồng thời cung cấp kiểm tra kiểu an toàn tại thời điểm biên dịch. Generics rất hữu ích khi bạn muốn tạo ra các cấu trúc dữ liệu hoặc các hàm có thể xử lý nhiều loại dữ liệu mà không cần phải viết lại mã cho từng loại.
Cú pháp cơ bản của Generics
Cú pháp của generics trong TypeScript sử dụng các tham số kiểu (type parameters) được định nghĩa trong dấu ngoặc nhọn (<>
). Bạn có thể sử dụng các tham số này trong các hàm, lớp hoặc giao diện để chỉ định loại dữ liệu sẽ được sử dụng.
Ví dụ về Generics với Hàm
Dưới đây là một ví dụ về cách sử dụng generics trong một hàm đơn giản:
function identity<T>(arg: T): T {
return arg;
}
const num = identity<number>(5); // Trả về 5
const str = identity<string>("Hello"); // Trả về "Hello"
Trong ví dụ này, hàm identity
sử dụng tham số kiểu T
. Tham số này cho phép hàm nhận bất kỳ loại dữ liệu nào và trả về cùng một loại. Khi gọi hàm, bạn có thể chỉ định loại dữ liệu bằng cách sử dụng cú pháp <T>
.
Ví dụ về Generics với Lớp
Generics cũng có thể được sử dụng trong các lớp, cho phép bạn tạo ra các lớp có thể làm việc với nhiều loại dữ liệu khác nhau:
class Box<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
public getValue(): T {
return this.value;
}
}
// Sử dụng lớp Box
const numberBox = new Box<number>(123);
console.log(numberBox.getValue()); // Output: 123
const stringBox = new Box<string>("Hello");
console.log(stringBox.getValue()); // Output: "Hello"
Trong ví dụ này, lớp Box
sử dụng tham số kiểu T
để lưu trữ một giá trị. Khi tạo một đối tượng của lớp Box
, bạn có thể chỉ định loại dữ liệu cụ thể mà bạn muốn lưu trữ.
Ví dụ về Generics với Giao diện
Generics cũng có thể được sử dụng trong giao diện để tạo ra các cấu trúc dữ liệu linh hoạt:
interface Pair<K, V> {
key: K;
value: V;
}
const numberStringPair: Pair<number, string> = { key: 1, value: "One" };
const booleanNumberPair: Pair<boolean, number> = { key: true, value: 100 };
Trong ví dụ này, giao diện Pair
sử dụng hai tham số kiểu K
và V
, cho phép bạn định nghĩa các cặp khóa-giá trị với các loại dữ liệu khác nhau.
Giới hạn Generics
Bạn có thể đặt giới hạn cho các tham số kiểu bằng cách sử dụng từ khóa extends
. Điều này cho phép bạn đảm bảo rằng tham số kiểu phải là một kiểu cụ thể hoặc một kiểu con của kiểu cụ thể:
function logLength<T extends { length: number }>(arg: T): void {
console.log(arg.length);
}
logLength("Hello"); // Output: 5
logLength([1, 2, 3]); // Output: 3
// logLength(123); // Lỗi: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
Trong ví dụ này, hàm logLength
chỉ nhận các tham số có thuộc tính length
, cho phép bạn gọi hàm với chuỗi hoặc mảng.
Kết luận
Generics trong TypeScript là một tính năng mạnh mẽ cho phép bạn tạo ra mã linh hoạt và tái sử dụng. Bằng cách sử dụng tham số kiểu, bạn có thể xây dựng các hàm, lớp và giao diện có khả năng xử lý nhiều loại dữ liệu khác nhau mà vẫn đảm bảo an toàn kiểu tại thời điểm biên dịch. Việc hiểu và áp dụng generics sẽ giúp bạn viết mã sạch hơn, dễ bảo trì hơn và tăng cường khả năng mở rộng cho các ứng dụng của bạn.