TypeScript - Generic Classes



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.

Open Compiler
// 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.

Open Compiler
// 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.

Open Compiler
// 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.

Advertisements