Typescript: Generic type

Trước khi đọc bài viết này bạn hãy bỏ ra 5 phút để tìm hiểu về Typescript cơ bản.

Sau khi đọc xong bài viết này bạn sẽ hiểu tại sao Generics lại cần thiết trong Typescript cũng như những ngôn ngữ lập trình khác.

Tại sao lại cần Generics?

Hãy cùng xem xét hàm identity(). Hàm này đơn giản chỉ nhận vào 1 tham số và trả về nó

function identity(arg: number): number {
    return arg;
}

Vấn đề đang gặp phải là ta đang gán kiểu của giá trị đầu vào (argument type) và kiểu trả về (return type) đều là number. Do đó hàm này chỉ duy nhất được sử dụng cho kiểu number. Nó sẽ không thể mở rộng hay hoặc tái sử dụng cho những kiểu khác.

Chúng ta có thể xem xét việc sử dụng kiểu any vì nó chấp nhận tất cả các kiểu trả về. Nhưng nếu dùng any thì không thể phán đoán được kiểu trả về của hàm là gì từ đó thực hiện các xử lý tiếp theo. Cũng như làm giảm hiệu quả của trình biên dịch.

Sử dụng Generic type có thể giải quyết vấn đề này.

Sử dụng Generics

Trước khi sử dụng, chúng ta hãy cùng tìm hiểu Generic type là gì?

Theo như định nghĩa của TypeScript về Generic:

In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

Hay như định nghĩa của Oracle về Generic trong Java:

generic type is a generic class or interface that is parameterized over types

Hiểu đơn giản thì Generic type là việc cho phép truyền type vào components(function, class, interface) như là 1 tham số.
Điều này sẽ giúp các components mềm dẻo hơn. Tái sử dụng tốt hơn.

Quay lại với bài toán bên trên, hãy thử áp dụng Generic để giải quyết vấn đề này:

function identity<T> (arg: T): T {
    return arg;
}

Sau tên hàm chúng ta thêm 1 type variable T trong dấu ngoặc nhọn <> (angled brackets). Biến T bây giờ sẽ trở thành một placeholder cho 1 kiểu giá trị mà chúng ta muốn truyền vào hàm identity thay thế cho kiểu number.

Type variable còn được biết đến với tên gọi khác là type parameters hay generic parameter

Biến T được viết tắt của từ Type. Type variable đầu tiên thường được đặt là T. Nhưng trong thực tế bạn có thể dùng bất cứ tên gì miễn là nó dễ hiểu.

 Generics có thể “extends”

Chúng ta có thể giới hạn phạm vi của Type variable với từ khoá extends

class A {
  attribute: string
}
class B extends A {}
class C {}

function identity2<T extends A>(arg: T): T {
  return arg;
}

identity2(new A()) // is valid
identity2(new B()) // is valid
// identity2(new C()) // is NOT valid: Argument of type 'C' is not assignable to parameter of type 'A'.

Generics có thể có “default type value”

Bạn còn có thể set default value cho Type variable nữa nhé!

//Generics can have a “default type value”

interface PersonD<T = string> {
  name:T
}

const james: PersonD = {
  name:'James'
}

const robin: PersonD<number> = {
  name:123
}

// const james: PersonD = {
//   name: 123 // is NOT valid: The expected type comes from property 'name' which is declared here on type 'PersonD<string>'
// }

Vậy khi nào thì sử dụng Generics?

Generics cho phép chúng ta tạo những components theo phong cách type-safe (type-safe way). Thông thường khi gặp những trường hợp nàythì chúng ta nên dùng Generics:

  • Khi hàm, class, interface của bạn làm việc với các kiểu dữ liệu đa dạng.
  • Khi hàm, class, interface của bạn sử dụng những kiểu dữ liệu đó ở 1 số chỗ bên trong các đối tượng này.

Hy vọng sau khi đọc xong bài viết này bạn sẽ có 1 cái nhìn khái quát về Generics và có thể áp dụng nó trong Typescript.

Tài liệu tham khảo

Add a Comment

Scroll Up