- vừa được xem lúc

Generics trong TypeScript

0 0 25

Người đăng: Phan Lý Huỳnh

Theo Viblo Asia

TypeScript là một ngôn ngữ mã nguồn mở dựa trên JavaScript, một trong những ngôn ngữ phổ biến và được sử dụng nhiều nhất. Typescript mở rộng thêm Javascript bằng cách thêm vào một số static types.

Types cung cấp một phương thức tường minh hơn để mô tả các hình thái của object, mô tả documentation tốt hơn, thông qua đó TypeScript có thể xác định rằng code của chúng ta đang hoạt động chính xác hay không. Trong bài viết này chúng ta cùng nhau tìm hiểu về Generics trong TypeScript.

Generics là gì?

Generics là một tính năng trong TypeScript và các ngôn ngữ lập trình khác, cho phép chúng ta viết một function, class hay interface chung cho nhiều loại dữ liệu khác nhau, và chỉ xác định loại dữ liệu cụ thể khi sử dụng loại dữ liệu đó.

Cú pháp Generics

Generics trong TypeScript bên trong dấu ngoặc nhọn, ở định dạng <T>, trong đó T đại diện cho type được truyền vào. <T> có thể được đọc dưới dạng chung của type T.

Trong trường hợp này, T sẽ hoạt động giống như cách các tham số hoạt động trong các hàm, dưới dạng placeholders cho một type sẽ được khai báo khi một instance của cấu trúc được tạo. Do đó, generics type được chỉ định bên trong dấu ngoặc nhọn còn được gọi là generic type parameters hoặc chỉ type parameters. Multiple generic types cũng có thể xuất hiện trong một định nghĩa, chẳng hạn như <T, K, A>.

Ví dụ:

function identity<T>(arg: T): T { return arg;
} const output1 = identity<string>("myString"); // type of output will be 'string'
console.log(output1); const output2 = identity<number>(123); // type of output will be 'number'
console.log(output2);

Tạo mới instance generics class

Để tạo một instance của một generic class, chúng ta cần dựng class và báo cho compiler biết thông qua cú pháp <>. Chúng ta có thể sử dụng bất kỳ type nào cho type T trong generic syntax, bao gồm base types, classes hoặc interface.

Ví dụ:

class GenericClass<T> { field: T; constructor(field: T) { this.field = field; }
} const instance1 = new GenericClass<number>(123);
console.log(instance1.field); // Output: 123, type of output will be 'number'

Trong ví dụ trêm. chúng ta đã tạo một generic class với một tham số kiểu dữ liệu T. Khi tạo một thể hiện của class này, chúng ta cần chỉ định kiểu dữ liệu cụ thể mà chúng ta muốn sử dụng (trong trường hợp này là number)

Hạn chế type T

Khi sử dụng generics, đôi khi nên hạn chế type T chỉ là một type cụ thể, hoặc tập hợp con của các types. Trong những trường hợp này, chúng ta không muốn code generic của mình có sẵn cho bất kỳ type của đối tượng nào, chúng ta chỉ muốn nó cho một tập hợp con cụ thể của các đối tượng. TypeScript sử dụng tính kế thừa để thực hiện điều này với generics.

Ví dụ 1:

class Shape { width: number; height: number;
} class GenericClass<T extends Shape> { field: T; setField(field: T): void { this.field = field; } getField(): T { return this.field; }
} const instance = new GenericClass<Shape>();
instance.setField({width: 100, height: 200});
console.log(instance.getField()); // Output: { width: 100, height: 200 }

Trong ví dụ trên, chúng ta đã tạo một generic class GenericClass với kiểu T thừa kế từ Shape class. Điều này có nghĩa là chỉ các instance của Shape class hoặc lớp kế thừa từ Shape mới được phép truyền vào generic class này

Ví dụ 2:

class GenericClass<T extends number | string> { field: T; setField(field: T): void { this.field = field; } getField(): T { return this.field; }
} const instance = new GenericClass<string>();
instance.setField("Hello World");
console.log(instance.getField()); // Output: "Hello World"

Ở ví dụ 2 trên, chúng ta cũng sử dụng extends để chỉ định T chỉ có thể là number hoặc string. Điều này có nghĩa là khi tạo một thể hiện của class, chúng ta chỉ có thể chỉ định kiểu dữ liệu là number hoặc string.

Generic interfaces

Chúng ta cũng có thể sử dụng interfaces với generic type syntax.

ví dụ:

interface GenericInterface<T> { field: T; setField(field: T): void; getField(): T;
} class ImplementationClass implements GenericInterface<number> { field: number; setField(field: number): void { this.field = field; } getField(): number { return this.field; }
} const instance = new ImplementationClass();
instance.setField(123);
console.log(instance.getField()); // Output: 123

Trong ví dụ này thì chúng ta đã tạo một interface chung là GenericInterface với type T. Sau đó tạo một class ImplementationClass implements interface GenericInterface và chỉ định string là type T. Khi sử dụng class ImplementationClass, chúng ta phải tuân theo các yêu cầu của generic interface, bao gồm thuộc tính, phương thức và kiểu dữ liệu cụ thể.

Tạo các đối tượng mới trong generics

Đôi khi, các generic classes có thể cần tạo một object thuộc loại được truyền vào như là type T.

Ví dụ:

class FirstClass { id: number = 10;
}
class SecondClass { name: string = 'my name';
}
class GenericCreator< T > { create(): T { return new T(); }
} var creator1 = new GenericCreator<FirstClass>();
var firstClass: FirstClass = creator1.create();
var creator2 = new GenericCreator<SecondClass>();
var secondClass : SecondClass = creator2.create();

Ở ví dụ trên, chúng ta định nghĩa 2 class: FirstClass, SecondClass. Sau đó chúng ta có một generic class và một function create. Create function này để tạo một instance mới của type T. Ở 4 dòng cuối của ví dụ chúng ta muốn sử dụng GenericCreator class để tạo instance mới.

Khi chạy ở ví dụ trên chúng ta sẽ tạo ra lỗi biên dịch TypeScript

Theo tài liệu TypeScript, để cho phép một generic class tạo các đối tượng thuộc type T, chúng ta cần tham chiếu đến type T bằng hàm khởi tạo của nó. Nên trong ví dụ trên create function cần được viết lại như sau:

class GenericCreator< T > { create(c: { new(): T }) : T { return new c(); }
}

Chúng ta sẽ chia create function thành các phần cấu thành nó. Đầu tiên là một đối số được truyền vào, tên là c. Đối số này được định nghĩa là thuộc type {new(): T}. Đây là cách cho phép chúng ta tham chiếu đến T bằng hàm khởi tạo của nó. Sau đó, chúng ta định nghĩa một anonymous type mới overloads new() function để có một constructor trả về type T. Mục đích của function này đơn giản là trả về một instance mới của biến c.

Sau khi viết lại create function giúp chúng bỏ lỗi biên dịch trước. Tuy nhiên, thay đổi này chúng ta phải truyền class definition tới create function, như sau:

var creator1 = new GenericCreator<FirstClass>();
var firstClass: FirstClass = creator1.create(FirstClass);
console.log(firstClass.id) // output: 10 var creator2 = new GenericCreator<SecondClass>();
var secondClass : SecondClass = creator2.create(SecondClass);
console.log(secondClass.name) // output: "my name"

any và generics type

Cả anygenerics đều là cách để chỉ định kiểu dữ liệu trong TypeScript. Tuy nhiên, hai cách này có một số khác biệt quan trọng.

  • any: any là một kiểu chung, nghĩa là bất kỳ giá trị nào cũng có thể được gán cho một biến hoặc tham số có kiểu any. Điều này làm cho chúng ta không có sự kiểm soát về kiểu dữ liệu hoặc tính toàn vẹn của dữ liệu, vì vậy việc sử dụng any có thể dẫn đến một số lỗi trong quá trình phát triển.
  • generics: Generics là một cách để chỉ định một kiểu chung cho một class, interface hoặc function. Generics type cho phép chúng ta chỉ định đối tượng cụ thể cần phải có một tập hợp các thuộc tính hoặc phương thức và hạn chế kiểu dữ liệu mà chúng ta muốn sử dụng. Điều này cho phép chúng ta có sự kiểm soát về kiểu dữ liệu và tính toàn vẹn của dữ liệu, giúp tránh lỗi trong quá trình phát triển.

Vì vậy, nếu muốn kiểm soát kiểu dữ liệu và giảm tình trạng lỗi trong code, chúng ta nên sử dụng generics thay vì any.

Kết luận

Trong bài viết này chúng ta cùng nhau tìm hiểu về Generics type trong TypeScript. Trong bài viết sau chúng ta cùng nhau tìm hiểu về mixins pattern sử dụng generics type với tính kế thừa. Cảm ơn các bạn đã theo dõi bài viết ❤️

Tài liệu tham khảo

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 528

- vừa được xem lúc

Type annotation vs Type Inference - Typescript

Trong bài viết này, chúng ta sẽ tìm hiểu kỹ về TypeScript bằng cách tìm hiểu sự khác biệt giữa kiểu chú thích và kiểu suy luận. Tôi sẽ cho rằng bạn có một số kinh nghiệm về JavaScript và biết về các kiểu cơ bản, như chuỗi, số và boolean.

0 0 45

- vừa được xem lúc

Type Annotation và Type Inference trong TypeScript là gì?

Khi làm việc với javascript chắc hẳn các bạn đã quá quen với việc dùng biến mà không cần phải quan tâm đến kiểu dữ liệu của nó là gì phải không? Đúng là mới đầu tiếp cận với Typescript mình cũng cảm thấy nó khá là phiền vì cần phải khai báo đủ type để nó chặt chẽ hơn. Lúc đó mình còn nghĩ: " JavaScr

0 0 37

- vừa được xem lúc

Tìm hiểu TypeScript và kiến thức cơ bản

TypeScript là gì. TypeScript sử dụng tất cả các tính năng của của ECMAScript 2015 (ES6) như classes, modules.

0 0 53

- vừa được xem lúc

TypeScript - P1: Vì sao TypeScript được yêu thích đến vậy?

Trải nghiệm thực tế. Trước khi là một Web Developer, tôi là một Mobile Developer và Java là thứ mà tôi từng theo đuổi.

0 1 69

- vừa được xem lúc

4 Tính năng rất hay từ TypeScript

Xin chào các bạn hôm nay mình xin chia sẽ những tính năng rất hay của TypeScript (TS), các bạn cùng tìm hiểu nhé. Ngoài việc set Type cho biến, tham số hay function thì ví dụ khi bạn nhìn vào một tham

0 0 96