Generics Cơ Bản
- Hàm Generics
Hàm generics cho phép bạn định nghĩa một hàm mà kiểu của tham số và kiểu trả về không được chỉ định cụ thể. Thay vào đó, bạn sử dụng một tham số kiểu (type parameter) để đại diện cho kiểu đó.
Ví dụ: Hàm Identity
typescript
Copy code
function identity(arg: T): T {
return arg;
}
: Đây là tham số kiểu (type parameter). T có thể là bất kỳ kiểu nào, và TypeScript sẽ xác định nó khi bạn gọi hàm.
arg: T: Tham số đầu vào của hàm có kiểu T.
T: Kiểu trả về của hàm cũng là T.
Sử Dụng Hàm Generics
typescript
Copy code
let result1 = identity('Hello, TypeScript'); // result1 có kiểu string
let result2 = identity(42); // result2 có kiểu number
Trong ví dụ trên, identity sẽ khiến TypeScript xác định T là string, vì vậy hàm sẽ nhận và trả về một giá trị kiểu string.
Tương tự, identity sẽ nhận và trả về một giá trị kiểu number.
Generics với Lớp (Class)
Generics cũng có thể được sử dụng trong các lớp để tạo các lớp có thể hoạt động với nhiều kiểu dữ liệu khác nhau mà không cần phải tạo nhiều lớp khác nhau.
Ví dụ: Lớp Box Generics
typescript
Copy code
class Box {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
Box: Đây là một lớp generics với tham số kiểu T.
private value: T: Thuộc tính value của lớp có kiểu T.
getValue(): T: Phương thức getValue trả về giá trị của thuộc tính value, có kiểu T.
Sử Dụng Lớp Generics
typescript
Copy code
let stringBox = new Box('Hello, TypeScript');
console.log(stringBox.getValue()); // In ra: Hello, TypeScript
let numberBox = new Box(123);
console.log(numberBox.getValue()); // In ra: 123
Generics với Giao Diện (Interface)
Generics có thể được sử dụng trong giao diện để định nghĩa các cấu trúc dữ liệu linh hoạt.
Ví dụ: Giao Diện Pair Generics
typescript
Copy code
interface Pair {
first: T;
second: U;
}
let pair: Pair = { first: 'TypeScript', second: 2024 };
console.log(pair); // In ra: { first: 'TypeScript', second: 2024 }
Pair: Giao diện generics với hai tham số kiểu T và U.
first: T và second: U: Các thuộc tính có kiểu T và U tương ứng.
Constraints (Ràng Buộc) Trong Generics
Bạn có thể đặt ràng buộc cho tham số kiểu để đảm bảo rằng kiểu dữ liệu truyền vào phải đáp ứng các yêu cầu nhất định.
Ví dụ: Ràng Buộc Generics
typescript
Copy code
interface Lengthwise {
length: number;
}
function logLength(arg: T): void {
console.log(arg.length);
}
T extends Lengthwise: Ràng buộc kiểu T phải có thuộc tính length kiểu number.
Sử Dụng Ràng Buộc
typescript
Copy code
logLength('Hello, TypeScript'); // In ra: 18
logLength([1, 2, 3]); // In ra: 3
// logLength(42); // Sẽ lỗi, vì số không có thuộc tính length
Tóm Tắt
Generics giúp viết mã linh hoạt và tái sử dụng bằng cách cho phép bạn sử dụng các tham số kiểu.
Hàm Generics cho phép bạn định nghĩa hàm có thể hoạt động với nhiều kiểu dữ liệu khác nhau.
Lớp Generics cho phép bạn định nghĩa các lớp hoạt động với nhiều kiểu dữ liệu.
Giao Diện Generics giúp định nghĩa cấu trúc dữ liệu linh hoạt.
Ràng Buộc Generics đảm bảo rằng các kiểu dữ liệu truyền vào phải đáp ứng các yêu cầu nhất định.
Hi vọng rằng các ví dụ trên giúp bạn hiểu rõ hơn về cách sử dụng Generics trong TypeScript!
Top comments (1)
Mapped Types và Conditional Types trong TypeScript
Ví Dụ: Readonly
typescript
Copy code
type Readonly = {
readonly [K in keyof T]: T[K];
};
type Readonly: Đây là định nghĩa của một kiểu mới Readonly sử dụng Generics với tham số kiểu T.
readonly [K in keyof T]: Mapped Type này ánh xạ qua tất cả các thuộc tính của kiểu T. K là tên của các thuộc tính trong T.
T[K]: Đây là kiểu dữ liệu của thuộc tính K trong T.
Giải Thích:
keyof T: Trả về tất cả các thuộc tính của kiểu T dưới dạng các tên thuộc tính (key).
readonly [K in keyof T]: Tạo một kiểu mới trong đó tất cả các thuộc tính của T đều được đánh dấu là readonly, tức là không thể thay đổi sau khi đã gán giá trị.
Sử Dụng Readonly
typescript
Copy code
interface Person {
name: string;
age: number;
}
const person: Readonly = {
name: 'Alice',
age: 30,
};
// person.name = 'Bob'; // Lỗi: Không thể gán giá trị vì thuộc tính 'name' là readonly
Trong ví dụ trên, Readonly tạo ra một kiểu mới dựa trên Person nhưng tất cả các thuộc tính đều là readonly, nghĩa là chúng không thể bị thay đổi sau khi được khởi tạo.
Ví Dụ: TrueType
typescript
Copy code
type TrueType = true extends true ? 'Yes' : 'No'; // 'Yes'
type TrueType: Đây là định nghĩa của một kiểu mới TrueType.
true extends true: Đây là điều kiện. Nếu true (trái) mở rộng từ true (phải), thì kết quả sẽ là 'Yes'.
'Yes' : 'No': Đây là hai giá trị khả dụng. Nếu điều kiện là đúng, kết quả là 'Yes', nếu không thì là 'No'.
Giải Thích:
true extends true: Luôn đúng vì true luôn mở rộng từ true.
Vì điều kiện là đúng, kiểu TrueType sẽ là 'Yes'.
Ví Dụ Khác:
typescript
Copy code
type IsString = T extends string ? 'Yes' : 'No';
type Result1 = IsString; // 'Yes'
type Result2 = IsString; // 'No'
type IsString: Định nghĩa một loại điều kiện để kiểm tra xem T có phải là string không.
T extends string ? 'Yes' : 'No': Nếu T mở rộng từ string, thì IsString sẽ là 'Yes', ngược lại là 'No'.
Sử Dụng Conditional Types:
typescript
Copy code
type Animal = 'dog' | 'cat';
type AnimalSounds = T extends 'dog' ? 'bark' : 'meow';
type DogSound = AnimalSounds<'dog'>; // 'bark'
type CatSound = AnimalSounds<'cat'>; // 'meow'
AnimalSounds: Nếu T là 'dog', thì kiểu sẽ là 'bark', nếu không thì là 'meow'.
Tóm Tắt
Mapped Types: Cho phép bạn tạo kiểu mới bằng cách ánh xạ qua các thuộc tính của một kiểu dữ liệu đã có. Ví dụ, kiểu Readonly tạo ra một kiểu mới với tất cả các thuộc tính là readonly.
Conditional Types: Cho phép bạn định nghĩa kiểu dựa trên điều kiện. Bạn có thể tạo các kiểu dữ liệu khác nhau dựa trên việc điều kiện có đúng hay không.
Cả hai kỹ thuật này đều rất hữu ích trong việc tạo kiểu dữ liệu linh hoạt và mạnh mẽ trong TypeScript.