Đã bao giờ bạn phải hì hục viết đi viết lại những hàm y hệt nhau chỉ vì chúng nhận vào các kiểu dữ liệu khác nhau chưa? Hoặc tệ hơn, bạn nhắm mắt dùng bừa kiểu any để code chạy cho qua chuyện, và rồi "trả giá" bằng những đêm thức trắng debug lỗi undefined trên production. Đây là nỗi đau chung của rất nhiều người khi mới chuyển từ JavaScript sang hệ sinh thái TypeScript. May mắn thay, chủ đề TypeScript Generics giải thích dễ hiểu có ví dụ dưới đây chính là "vị cứu tinh" giúp bạn giải quyết triệt để vấn đề đó. Tại Phạm Hải, qua quá trình hỗ trợ hàng nghìn lập trình viên, chúng mình nhận thấy làm chủ Generics là bước nhảy vọt quan trọng để viết code linh hoạt, an toàn và dễ bảo trì hơn rất nhiều.
So what exactly is TypeScript Generics that is so "divine"?
TypeScript Generics là gì? Hiểu một cách đơn giản, nó là một công cụ mạnh mẽ cho phép bạn tạo ra các thành phần (hàm, class, interface) có thể làm việc với nhiều loại kiểu dữ liệu khác nhau, mà vẫn giữ nguyên được sự kiểm tra kiểu chặt chẽ của trình biên dịch.
If you have ever read the Learn Basic JavaScript for Beginners 2026 roadmap, you will know that JavaScript is very flexible in accepting any input data. However, that laxity in a large project environment is the seed of countless hidden bugs. Generics were born to bring the perfect balance: providing the flexibility of JavaScript, but being extremely safe like traditional static typing languages.
Bản chất của khái niệm Generics là giúp chúng ta xây dựng các thành phần tái sử dụng (reusable components) chuẩn mực. Thay vì ép buộc một hàm chỉ nhận duy nhất kiểu string hay number, bạn cho phép nó nhận một "kiểu dữ liệu ẩn số" – thứ sẽ được xác định cụ thể vào thời điểm bạn gọi hàm đó.
Simply put: Generics are "parameters for data types"
Imagine Generics are like parameters of a regular function, but instead of passing a value, you pass in a type parameter to define the structure of the data.
Khi viết hàm, bạn thường dùng các biến như x, y để đại diện cho giá trị truyền vào. Với hướng dẫn Generics TypeScript, chúng ta sử dụng các chữ cái in hoa như T (Type), K (Key), V (Value) được đặt trong cặp dấu ngoặc nhọn < > để đại diện cho kiểu dữ liệu. Khi bạn thực thi hàm, bạn mới quyết định T là string, number hay một object user cụ thể. Cơ chế này giúp hệ thống suy luận kiểu (type inference) của TypeScript hoạt động cực kỳ chính xác và mượt mà.
Ví dụ kinh điển: Hàm identity phiên bản "trước và sau" khi có Generics
Dưới đây là ví dụ về Generics trong TypeScript thông qua hàm identity - một hàm nhận vào giá trị nào thì trả về đúng giá trị đó. Đây là ví dụ kinh điển nhất để minh họa sức mạnh của công cụ này.
Suppose you need to write a function that returns the argument passed in. If you don't use Generics, you will have to write it manually like this:
function identityNumber(arg: number): number {
return arg;
}
function identityString(arg: string): string {
return arg;
}
As you can see, the possibility of code reuse here is zero. You have to repeat the exact same logic for each data type. But when applying Generics TypeScript syntax, things become much more elegant:
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("Chào bạn"); // T được xác định là string
let output2 = identity<number>(2026); // T được xác định là number
With just one function, you can handle all data types! For those who are in the Learning TypeScript from scratch for JavaScript developer stage, this is the "Aha!" moment! helps you clearly see the optimization of this language compared to pure JavaScript.
So sánh "một trời một vực": Generics vs any - Tại sao dùng any là một cái bẫy?
Việc phân biệt Generics và any trong TypeScript là bài học vỡ lòng cực kỳ quan trọng; dùng any sẽ tắt hoàn toàn bộ kiểm tra kiểu, khiến code dễ sinh lỗi runtime, trong khi Generics giữ lại toàn bộ thông tin kiểu dữ liệu đầu vào để bảo vệ đầu ra.
Many frontend programmers new to TypeScript often have the habit of writing quickly like this:
function identityAny(arg: any): any {
return arg;
}
Lúc này, kiểu any đã phá vỡ mọi hàng rào bảo vệ của trình biên dịch. Nếu bạn truyền vào một chuỗi (string), TypeScript không hề biết kết quả trả về là chuỗi. Bạn có thể vô tình gọi hàm .toFixed() (một hàm chỉ dành cho số) lên kết quả đó mà VS Code không hề gạch đỏ cảnh báo, dẫn đến lỗi sập ứng dụng khi người dùng sử dụng.
Ngược lại, lợi ích của Generics TypeScript là nó duy trì tính an toàn kiểu dữ liệu tuyệt đối. Khi dùng <T>, TypeScript "nhớ" chính xác T là gì và áp dụng nghiêm ngặt các luật lệ của kiểu đó xuyên suốt quá trình thực thi.
| Criteria | Sử dụng kiểu any |
Sử dụng Generics <T> |
|---|---|---|
| Kiểm tra lỗi lúc gõ code | None (Skip completely) | Yes (Immediate alert) |
| Gợi ý code (IntelliSense) | Inactive | Works 100% accurate |
| Mức độ an toàn | Very low (Easy to cause runtime bugs) | Very high (Protected from compile time) |
The main "stage" where Generics shine is in TypeScript
You can apply how to use Generics in TypeScript in three main areas: Functions, Interfaces, and Classes, helping the entire project architecture become coherent and consistent.
Based on the latest updates as of 2026 (with TypeScript versions 5.8 and 6.0), Generics remain the backbone of every major library. Let's see how they work in real, everyday situations.
Generic Functions: The heart of reuse
Hàm Generic trong TypeScript cho phép bạn viết các logic xử lý mảng, object, hoặc gọi API một lần duy nhất và áp dụng một cách an toàn cho mọi kiểu dữ liệu trả về.
During the process of building a web application, communication with the server is inevitable. If you are looking into What is a REST API and RESTful design, you will realize that shared data fetch functions require the power of Generics to shape the returned data.
Example of a generic API call:
async function fetchApi<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) throw new Error('Network error');
return await response.json();
}
// Sử dụng trong thực tế:
interface UserProfile { id: number; name: string; email: string; }
// Lúc này 'userData' được TypeScript hiểu chắc chắn là kiểu UserProfile
const userData = await fetchApi<UserProfile>('/api/users/1');
Generic Interface (Generic Interfaces): Shapes flexible data structures
Interface Generic trong TypeScript giúp định nghĩa các cấu trúc dữ liệu linh hoạt, nơi mà một hoặc nhiều thuộc tính bên trong có thể thay đổi kiểu tùy theo ngữ cảnh sử dụng cụ thể.
When you build complex applications, it's very common to create response wrappers from APIs.
interface ApiResponse<T> {
status: number;
message: string;
data: T; // Dữ liệu thực tế sẽ biến đổi tùy API
}
Bây giờ, thuộc tính data có thể là một mảng User[], hoặc một object Product, hoặc bất cứ thứ gì bạn muốn. Khi kết hợp với lộ trình Học React JS từ đầu cho người mới, việc dùng Interface Generics cho Props hoặc State của các custom Component sẽ giúp bạn tránh được vô số lỗi lặt vặt. Ngoài ra, nó cũng rất hữu ích khi bạn làm việc với type alias hay các utility types có sẵn của TypeScript (như Partial<T>, Readonly<T>).
Generic Class (Generic Classes): Build classes that can work with all types of data
Class Generic trong TypeScript cho phép khởi tạo các đối tượng với kiểu dữ liệu được chỉ định ngay lúc new instance, cực kỳ phổ biến trong việc thiết kế các cấu trúc dữ liệu như Stack, Queue hay Repository.
Here is an example of a simple DataStorage class:
class DataStorage<T> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
removeItem(item: T) {
this.data.splice(this.data.indexOf(item), 1);
}
getItems(): T[] {
return [...this.data];
}
}
const textStorage = new DataStorage<string>();
textStorage.addItem("Phạm Hải");
// textStorage.addItem(2026); // Trình biên dịch sẽ báo lỗi ngay lập tức!
If you are working on the server side and intend to Learn Node.js from scratch for backend developers, applying Class Generics in Services or Repository patterns is an almost mandatory standard to keep the code base clean and easy to maintain.
Enhance Generics with Constraints (Generic Constraints)
Sometimes the freedom of Generics is too open, and now you need to use Generic constraints (Constraints) to strictly limit which data types are allowed to be passed in, ensuring they must contain the necessary properties.
TypeScript Generics cơ bản rất tốt, nhưng đôi khi bạn cần đưa nó vào một "khuôn khổ" kỷ luật hơn để code hoạt động đúng logic.
Why do we need binding? The problem when generics are too "free"
Khi một tham số kiểu T có thể là bất cứ thứ gì trên đời, TypeScript sẽ không cho phép bạn truy cập vào một thuộc tính cụ thể (ví dụ như .length) vì nó không dám chắc kiểu dữ liệu nào cũng sở hữu thuộc tính đó.
Consider the following error-causing example:
function logLength<T>(arg: T): T {
// LỖI: Property 'length' does not exist on type 'T'.
console.log(arg.length);
return arg;
}
Bởi vì T có thể là một số number (một kiểu không hề có thuộc tính length), TypeScript đã chủ động ngăn chặn dòng code này. Đây chính là lý do chúng ta cần đến các ràng buộc.
Cú pháp extends: "Bắt" Generic phải tuân theo một "khuôn khổ" nhất định
Bằng cách sử dụng từ khóa extends, chúng ta tạo ra các ràng buộc Generics, yêu cầu tham số kiểu T bắt buộc phải kế thừa hoặc chứa một cấu trúc interface nhất định.
We will fix the above error function as follows:
interface HasLength {
length: number;
}
// T bắt buộc phải có thuộc tính 'length'
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length); // VS Code không còn báo lỗi!
return arg;
}
logLength("Hello 2026"); // Hợp lệ (string có length)
logLength([1, 2, 3]); // Hợp lệ (array có length)
// logLength(100); // Lỗi: number không có length
Trong các framework backend hiện đại mang tính cấu trúc cao, điển hình như khi bạn sử dụng NestJS framework Node.js chuyên nghiệp, kỹ thuật ràng buộc extends này được sử dụng liên tục để validate dữ liệu đầu vào (DTO) một cách tự động và an toàn.
"Bloody" experience: When should and should not use Generics?
Knowing exactly when to use Generics in TypeScript is just as important as knowing how to write its syntax. Don't overuse generics if your code only handles a single, fixed data type.
With many years of practical experience at Pham Hai, we realize that new developers often fall into the trap of "Genericizing" everything, making the code confusing. Below is a guide for you.
Recommended when: You find yourself repeating code for different styles
Nếu bạn nhận ra mình đang phải copy-paste và viết 3 hàm giống hệt nhau, chỉ khác mỗi chữ string, number, boolean ở phần khai báo tham số, đó là tín hiệu vũ trụ mách bảo bạn hãy dùng Generics ngay lập tức.
Complying with the DRY (Don't Repeat Yourself) principle will help your project shrink in size significantly. Generics were born exactly to serve this optimization purpose.
Should be used when: Building reusable components (components, utilities)
A company's generic UI libraries, utility helper functions (like sorting arrays, clone objects), or custom hooks in React are the best places to implement Generics.
It helps these components become maximally flexible, meeting the diverse needs of other developers in the team (who will reuse your functions) without sacrificing system safety.
Should not be used when: Functions or classes only work with a single data type
Nếu một hàm được sinh ra chỉ với mục đích duy nhất là tính tổng 2 số nguyên, hãy dùng thẳng kiểu number. Đừng cố nhét <T> vào chỉ để trông cho "nguy hiểm" hay ra vẻ chuyên nghiệp.
Overusing Generics in simple cases only makes the code difficult to read and slows down the onboarding process for newbies. Always remember, the ultimate goal of TypeScript is to make code clearer and more transparent, not to confuse the reader.
In short, TypeScript Generics easy-to-understand explanations with examples have shown us that this is absolutely not a high-level academic concept. It is an extremely practical, powerful tool and is the standard of the modern software industry. Mastering Generics, you will not only write code faster thanks to great reusability, but also "sleep better" every night knowing that your code is protected safely, with less risk of errors. It's the difference between someone who just "knows how to type" TypeScript and an engineer who truly "masters" his or her static type system.
Have you ever encountered a difficult problem in a project that Generics could be the key to solving? Try immediately applying this practical knowledge to your project and share the results in the comments section below! If you find the article useful, do not hesitate to share it for your team colleagues to read.
Lưu ý: Thông tin trong bài viết này chỉ mang tính chất tham khảo. Để có lời khuyên tốt nhất, vui lòng liên hệ trực tiếp với chúng tôi để được tư vấn cụ thể dựa trên nhu cầu thực tế của bạn.