Generics trong TypeScript
Bài viết này giải thích về generics trong TypeScript.
YouTube Video
Generics trong TypeScript
Generics trong TypeScript là một tính năng cho phép bạn định nghĩa các hàm, lớp, và giao diện có thể tái sử dụng và an toàn kiểu bằng cách tham số hóa các kiểu dữ liệu. Sử dụng generics cho phép bạn viết mã không phụ thuộc vào các kiểu dữ liệu cụ thể, cho phép bạn thực hiện các thao tác giống nhau trên nhiều kiểu khác nhau.
Kiến thức cơ bản về Generics
Generics hoạt động như các mẫu (template) nhận kiểu dữ liệu làm tham số, cho phép các hàm và lớp xử lý các kiểu dữ liệu khác nhau.
Hàm Generics
Dưới đây là một ví dụ về hàm với các kiểu tham số được chỉ định bằng generics.
1function identity<T>(value: T): T {
2 return value;
3}
4
5console.log(identity<number>(42)); // 42
6console.log(identity<string>("Hello")); // Hello
T
là một tham số kiểu generics đại diện cho kiểu của các tham số hàm và kiểu giá trị trả về. Kiểu thực tế được xác định khi hàm được gọi.- Bằng cách chỉ định rõ ràng
<number>
hoặc<string>
, bạn đang chỉ định kiểu.
Các kiểu generics hoạt động mà không cần chỉ định rõ ràng vì TypeScript thực hiện suy luận kiểu.
1function identity<T>(value: T): T {
2 return value;
3}
4
5console.log(identity(42)); // 42
6console.log(identity("Hello")); // Hello
Ràng buộc trên Generics
Bằng cách đặt ràng buộc lên generics, bạn có thể giới hạn chúng chỉ nhận các kiểu cụ thể.
1function loggingIdentity<T extends { length: number }>(arg: T): T {
2 console.log(arg.length);
3 return arg;
4}
5
6loggingIdentity("Hello"); // 5
7loggingIdentity([1, 2, 3]); // 3
8
9// loggingIdentity(42); // Error: number does not have a length property.
- Chỉ định
T extends { length: number }
cho thấy rằngT
phải là một kiểu có thuộc tínhlength
. Do đó, các kiểu không có thuộc tínhlength
sẽ không được chấp nhận.
Lớp Generics
Các lớp cũng có thể được định nghĩa bằng generics. Các lớp generics cung cấp các kiểu linh hoạt cho các thuộc tính và phương thức.
1class Box<T> {
2 private _value: T;
3
4 constructor(value: T) {
5 this._value = value;
6 }
7
8 public getValue(): T {
9 return this._value;
10 }
11
12 public setValue(value: T): void {
13 this._value = value;
14 }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue()); // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue()); // Hello
Box<T>
khai báo kiểuT
được sử dụng trong lớp như một generics. Điều này cho phép cùng một lớp được tái sử dụng cho các kiểu khác nhau.
Giao diện Generics
Generics cũng có thể được sử dụng với các giao diện.
1interface Pair<T, U> {
2 first: T;
3 second: U;
4}
5
6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
7console.log(numberStringPair); // { first: 1, second: 'One' }
8
9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair); // { first: true, second: [ 1, 2, 3 ] }
- Bằng cách chỉ định hai kiểu generics bằng
Pair<T, U>
, bạn có thể định nghĩa một đối tượng với sự kết hợp của các kiểu khác nhau.
Tham số kiểu mặc định
Cũng có thể chỉ định kiểu mặc định cho các tham số kiểu tổng quát.
1function createArray<T = string>(length: number, value: T): T[] {
2 return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a")); // ['a', 'a', 'a']
6console.log(createArray(3, 100)); // [100, 100, 100]
- Chúng tôi đặt tham số kiểu mặc định là
string
với<T = string>
. Nếu không chỉ định kiểu rõ ràng,T
sẽ có kiểu làstring
.
Bí danh kiểu tổng quát
Kiểu tổng quát cũng có thể được sử dụng làm bí danh kiểu (type
).
1type Result<T> = {
2 success: boolean;
3 data: T;
4};
5
6const successResult: Result<number> = { success: true, data: 42 };
7const errorResult: Result<string> = { success: false, data: "Error occurred" };
8
9console.log(successResult); // { success: true, data: 42 }
10console.log(errorResult); // { success: false, data: 'Error occurred' }
Result<T>
đại diện cho một đối tượng kết quả chứa dữ liệu có kiểuT
. Bằng cách này, bạn có thể tạo bí danh kiểu linh hoạt bằng cách sử dụng kiểu tổng quát.
Nhiều kiểu tổng quát
Bằng cách sử dụng nhiều kiểu tổng quát, bạn có thể định nghĩa các hàm và lớp linh hoạt hơn nữa.
1function merge<T, U>(obj1: T, obj2: U): T & U {
2 return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged); // { name: 'Alice', title: 'Engineer' }
- Hàm
merge
nhận hai kiểu khác nhauT
vàU
và kết hợp chúng để trả về một đối tượng mới.
Tóm tắt
- Kiểu tổng quát (Generics) cho phép mã có thể tái sử dụng và an toàn kiểu bằng cách xử lý kiểu như là tham số.
- Bằng cách sử dụng kiểu tổng quát trong hàm, lớp và giao diện, bạn có thể viết mã logic linh hoạt xử lý nhiều kiểu khác nhau.
- Bằng cách thêm giới hạn cho tham số kiểu hoặc đặt tham số kiểu mặc định, bạn có thể kiểm soát phạm vi của kiểu tổng quát.
Bằng cách sử dụng kiểu tổng quát, bạn có thể viết mã đa dụng, không phụ thuộc vào kiểu cụ thể, tận dụng hệ thống kiểu mạnh mẽ của TypeScript.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.