Hãy tưởng tượng Generic giống như một cái khuôn làm bánh. Bạn chỉ cần tạo ra một cái khuôn duy nhất, nhưng bạn có thể đổ bột mì để làm bánh mì, đổ sô-cô-la để làm bánh sô-cô-la, hoặc đổ thạch để làm bánh thạch…😅
List<T>, nếu bạn thay T bằng string, danh sách đó chỉ chứa chuỗi. Nếu bạn thay T bằng int, nó sẽ chứa số nguyên.Ví dụ 1: Hàm “Trả đồ” (Hàm nhận gì trả nấy)
Giả sử bạn muốn viết một hàm nhận vào một giá trị và trả về chính giá trị đó.
❌ Cách làm thủ công (Không dùng Generic)
Nếu không dùng Generic, bạn phải viết rất nhiều hàm cho từng kiểu dữ liệu, hoặc dùng any (nhưng dùng any thì TypeScript sẽ không còn tác dụng kiểm tra lỗi nữa).
function echoNumber(item: number): number { return item; }
function echoString(item: string): string { return item; }
Cách làm thông minh với Generic
Bạn dùng một chữ đại diện (thường là chữ <T>) để đại diện cho “Kiểu dữ liệu sẽ truyền vào sau”.
// Chữ T ở đây đóng vai trò như một biến chứa Kiểu Dữ Liệu
function echo<T>(item: T): T {
return item;
}
// Cách sử dụng:
let output1 = echo<string>("Xin chào các bạn"); // T bây giờ là string, hàm trả về string
let output2 = echo<number>(100); // T bây giờ là number, hàm trả về number
let output3 = echo<boolean>(true); // T bây giờ là boolean, hàm trả về boolean
Mổ xẻ 1 hàm generic
function echo<T>(item: T): T {
return item;
}
/*
1. KHAI BÁO BIẾN KIỂU
│
function echo<T>(item: T): T { ... }
│ │
2. ÁP KIỂU CHO ĐẦU VÀO ─────┘ └───── 3. ÁP KIỂU CHO ĐẦU RA
*/1. Chữ T thứ nhất: <T> (Khai báo tham số kiểu)
- Vị trí: Ngay sau tên hàm
echo. - Vai trò: Đây là nơi khai báo (đăng ký) với TypeScript rằng: “Hàm này là một hàm Generic, và tôi sẽ dùng chữ cái
Tnày làm đại diện cho kiểu dữ liệu sẽ được truyền vào sau này”. - Hình ảnh thực tế: Nó giống như việc bạn đặt tên cho một biến số (ví dụ:
x), nhưng ở đâyTlà một biến chứa kiểu dữ liệu (Type Variable). Bạn có thể đổi chữTnày thành bất kỳ chữ gì khác như<U>,<K>,<Type>đều được, miễn là các chữ sau đổi theo. Tuy nhiên nên để theo convention là chữ T
2. Chữ T thứ hai: item: T (Kiểu dữ liệu của tham số đầu vào)
- Vị trí: Bên trong dấu ngoặc đơn, đi kèm với tham số
item. - Vai trò: Quy định kiểu dữ liệu của đầu vào. Nghĩa là tham số
itembắt buộc phải có kiểu dữ liệu trùng với kiểuTđã khai báo ở trên. - Cách hoạt động: Khi bạn gọi hàm
echo("Hello"), TypeScript thấy đầu vào là một chuỗi (string). Nó sẽ lập tức gán giá trịstringcho biếnT. Lúc này,itemsẽ có kiểu làstring.
3. Chữ T thứ ba: : T (Kiểu dữ liệu của giá trị trả về)
- Vị trí: Sau dấu hai chấm
:của hàm (trước dấu mở ngoặc nhọn{). - Vai trò: Quy định kiểu dữ liệu của đầu ra (giá trị mà hàm trả về). Nó cam kết rằng hàm này sau khi xử lý xong sẽ trả về một kết quả có đúng kiểu dữ liệu
T. - Ý nghĩa: Nhờ chữ
Tnày, TypeScript biết chắc chắn rằng nếu bạn truyền vào mộtstring(item: T), bạn sẽ nhận lại chính xác mộtstring(: T), chứ không phải lànumberhayboolean.
Tóm gọn lại
- Khi nào thấy dấu
<T>(hoặc<U>,<K>), đó chính là Generic. - Nó là một “biến chứa kiểu dữ liệu”. Bạn truyền kiểu dữ liệu nào vào, toàn bộ chỗ nào có chữ
Tsẽ biến thành kiểu dữ liệu đó. - Nó giúp code vừa linh hoạt (dùng được cho nhiều kiểu) vừa an toàn (không lo bị sai kiểu dữ liệu lúc chạy).




