Generics
Generics là một tính năng cho phép bạn định nghĩa hàm, interface hoặc class mà không cần chỉ định trước kiểu cụ thể, và sau đó chỉ định kiểu cụ thể khi sử dụng.
Ví dụ đơn giản
Đầu tiên, chúng ta sẽ triển khai một hàm createArray
, nó có thể tạo ra một mảng có độ dài xác định, đồng thời điền tất cả các mục với một giá trị mặc định:
Trong ví dụ trên, chúng tôi đã sử dụng generics array mà chúng tôi đã đề cập trước đó để định nghĩa kiểu trả về.
Mã này không báo lỗi khi biên dịch, nhưng một nhược điểm rõ ràng là nó không xác định chính xác kiểu trả về:
Array<any>
cho phép mỗi mục trong mảng là bất kỳ kiểu nào. Nhưng mong đợi của chúng tôi là, mỗi mục trong mảng nên là kiểu của giá trị đầu vào.
Lúc này, generics sẽ có ích:
Trong ví dụ trên, chúng tôi đã thêm <T>
sau tên hàm, trong đó T
được sử dụng để chỉ đại diện cho bất kỳ kiểu đầu vào nào, sau đó nó có thể được sử dụng trong giá trị đầu vào value: T
và đầu ra Array<T>
.
Tiếp theo, khi bạn gọi nó, bạn có thể chỉ định rõ ràng rằng kiểu cụ thể của nó là string
. Tất nhiên, bạn cũng có thể không chỉ định một cách thủ công, mà để suy luận kiểu tự động suy ra:
Nhiều tham số kiểu
Khi định nghĩa generics, bạn có thể định nghĩa nhiều tham số kiểu cùng một lúc:
Trong ví dụ trên, chúng tôi đã định nghĩa một hàm swap
để hoán đổi các phần tử của mảng đầu vào.
Ràng buộc generics
Khi sử dụng biến generics bên trong hàm, bởi vì không biết trước nó là kiểu gì, nên bạn không thể tự do thao tác các thuộc tính hoặc phương thức của nó:
Trong ví dụ trên, generics T
có thể không bao gồm thuộc tính length
, do đó, nó sẽ báo lỗi khi biên dịch.
Lúc này, chúng ta có thể ràng buộc generics, chỉ cho phép hàm này chấp nhận những biến chứa thuộc tính length
. Đây chính là ràng buộc generics:
Trong ví dụ trên, chúng tôi đã sử dụng extends
để ràng buộc generics T
phải tuân thủ hình dạng của interface Lengthwise
, tức là phải chứa thuộc tính length
.
Bây giờ, nếu bạn gọi loggingIdentity
và arg
đầu vào không chứa length
, nó sẽ báo lỗi trong giai đoạn biên dịch:
Các tham số kiểu có thể ràng buộc lẫn nhau:
Trong ví dụ trên, chúng tôi đã sử dụng hai tham số kiểu, trong đó yêu cầu T
kế thừa từ U
, điều này đảm bảo rằng U
sẽ không xuất hiện các trường không tồn tại trong T
.
Generics Interface
Chúng ta đã học trước đó, có thể sử dụng interface để định nghĩa hình dạng mà một hàm cần phải tuân theo:
Tất nhiên, bạn cũng có thể sử dụng interface chứa generics để định nghĩa hình dạng của hàm:
Đi xa hơn, chúng ta có thể đưa tham số generics lên trước tên interface:
Lưu ý, khi sử dụng generics interface, bạn cần định nghĩa kiểu của tham số generics.
Generics Class
Tương tự như generics interface, generics cũng có thể được sử dụng trong định nghĩa kiểu của class:
Kiểu mặc định cho tham số generics
Bắt đầu từ TypeScript 2.3, chúng ta có thể chỉ định kiểu mặc định cho tham số kiểu trong generics. Khi sử dụng generics mà không chỉ định trực tiếp tham số kiểu trong mã và cũng không thể suy ra từ tham số giá trị thực tế, kiểu mặc định này sẽ được sử dụng.