”;
Generic Classes
TypeScript Generic classes allow you to create a class that can work over a variety of data types rather than a single one. It increases the scalability and reusability of the code. Let”s understand how generic classes work in TypeScript.
Syntax
You can follow the syntax below to use the generic classes in TypeScript.
class class_name<T, U> { // Class body } let obj1 = new class_name<data_type_1, data_type_2>();
-
In the above syntax, ”class” is a keyword to define a class.
-
”class_name” is a valid identifier, representing the class name.
-
”<T, U>” are type parameters specified in the angular bracket. You can specify as many as you want.
-
While defining the object of the class, you need to pass data types as an argument after the class name in the angular bracket.
Example
In the code below, we have defined the generic class named ”Box” which takes type parameter T.
In the class, we have defined the ”val” variable of type T, and the constructor function which initializes the value of the ”val” variable.
After that, we defined the getters and setters named get() and set(), respectively to get the value of the ”val” variable.
Next, we have defined the ”box1” and ”box2” objects of the Box class which take number and string data type as a type parameter argument, respectively.
// generic class class Box<T> { // member variable val: T; // constructor with value constructor(value: T) { this.val = value; } // Method to get value get(): T { return this.val; } // Method to set value set(value: T): void { this.val = value; } } // create object of Box class let box1 = new Box<number>(10); console.log(box1.get()); // 10 let box2 = new Box<string>("Hello"); console.log(box2.get()); // Hello
On compiling, it will generate the following JavaScript code:
// generic class class Box { // constructor with value constructor(value) { this.val = value; } // Method to get value get() { return this.val; } // Method to set value set(value) { this.val = value; } } // create object of Box class let box1 = new Box(10); console.log(box1.get()); // 10 let box2 = new Box("Hello"); console.log(box2.get()); // Hello
Output
The output of the above code is as follows –
10 Hello
Example
In the TypeScript code below:
-
We have defined the ”Stack” class which takes a type parameter ”T”.
-
In the class, we have defined the private variable ”st” whose type is an array of type T.
-
The constructor function initializes the ”st” array.
-
Push() method takes the element of type ”T” as a parameter and inserts it in the ”st” array.
-
The pop() method removes the last element from the ”st” array and returns it.
-
The peek() method returns the last element from the array.
-
The isEmpty() method returns a boolean value based on whether the array is empty.
-
The size() method returns the size of the ”st” array.
-
Next, we have defined the object of the Stack class with the number data type, performed various operations using the methods of the Stack class.
// Defining the class stack class Stack<T> { // Defining the private array to store the stack elements private st: T[] = []; // Constructor to initialize the stack with initial contents constructor(initialContents?: T[]) { if (initialContents) { this.st = initialContents; } } // Method to push an element to the stack push(item: T): void { this.st.push(item); } // Method to pop an element from the stack pop(): T | undefined { return this.st.pop(); } // Method to get the top element of the stack peek(): T | undefined { return this.st[this.st.length - 1]; } // Method to check if the stack is empty isEmpty(): boolean { return this.st.length === 0; } // Method to get the size of the stack size(): number { return this.st.length; } } // Usage Example const numberStack = new Stack<number>(); numberStack.push(1); numberStack.push(2); numberStack.push(3); console.log(numberStack.peek()); // Outputs: 3 console.log(numberStack.pop()); // Outputs: 3 console.log(numberStack.peek()); // Outputs: 2 console.log(numberStack.isEmpty()); // Outputs: false console.log(numberStack.size()); // Outputs: 2
On compiling, it will generate the following JavaScript code:
// Defining the class stack class Stack { // Constructor to initialize the stack with initial contents constructor(initialContents) { // Defining the private array to store the stack elements this.st = []; if (initialContents) { this.st = initialContents; } } // Method to push an element to the stack push(item) { this.st.push(item); } // Method to pop an element from the stack pop() { return this.st.pop(); } // Method to get the top element of the stack peek() { return this.st[this.st.length - 1]; } // Method to check if the stack is empty isEmpty() { return this.st.length === 0; } // Method to get the size of the stack size() { return this.st.length; } } // Usage Example const numberStack = new Stack(); numberStack.push(1); numberStack.push(2); numberStack.push(3); console.log(numberStack.peek()); // Outputs: 3 console.log(numberStack.pop()); // Outputs: 3 console.log(numberStack.peek()); // Outputs: 2 console.log(numberStack.isEmpty()); // Outputs: false console.log(numberStack.size()); // Outputs: 2
Output
The output of the above code is as follows –
3 3 2 false 2
Implementing Generic Interface with Generic Classes
Generic classes can also implement the generic interfaces. So, developers can use a single generic interface to implement multiple generic classes, allowing them to reuse the code.
Syntax
You can follow the syntax below to implement the generic interface with generic classes.
class class_name<T> implements interface_name<T> { // Class body }
-
In the above syntax, ”class class_name<T>” defines the generic class.
-
”implements” is a keyword to implement the interface with the class.
-
”interface_name<T>” is a generic interface.
Example
In the example below:
-
We have defined the generic interface named ”dataBase”, which defines the findById() and save() method.
-
Next, we have defined the generic class named ”memorydataBase”, and implemented it with the ”dataBase” interface.
-
In the class, we have defined the ”items” map which stores the numeric value as a key, the value of type ”T”.
-
Next, we have implemented the findById() method, which accesses the value by key from the map and returns it.
-
The save() method stores the key-value pair in the ”items” map.
-
In the end, we created the object of the ”MemorydataBase” class and performed various operations using this method.
// Defining a generic interface interface dataBase<T> { findById(id: number): T | undefined; save(item: T): void; } // Defining a class that implements the generic interface class MemorydataBase<T> implements dataBase<T> { // Defining a private property that is a map of items private items = new Map<number, T>(); // Implementing the findById method findById(id: number): T | undefined { return this.items.get(id); } // Implementing the save method save(item: T): void { const id = this.items.size + 1; this.items.set(id, item); } } // Creating an instance of the MemorydataBase class const repo = new MemorydataBase<string>(); repo.save("Hello"); console.log(repo.findById(1)); // Outputs: Hello
On compiling, it will generate the following JavaScript code:
// Defining a class that implements the generic interface class MemorydataBase { constructor() { // Defining a private property that is a map of items this.items = new Map(); } // Implementing the findById method findById(id) { return this.items.get(id); } // Implementing the save method save(item) { const id = this.items.size + 1; this.items.set(id, item); } } // Creating an instance of the MemorydataBase class const repo = new MemorydataBase(); repo.save("Hello"); console.log(repo.findById(1)); // Outputs: Hello
Output
The output of the above code is as follows –
Hello
You may use the ”extends” keyword to use the various constraints with the generic classes. It”s always a good idea to use generic parameters, constraints, interfaces, and classes in your code to make it scalable and reusable.
”;