TypeScript Generic Classes
Generics in TypeScript allow us to create reusable and type-safe components. Generic classes define a blueprint that can work with multiple data types without sacrificing type safety.
Syntax:
class GenericClass<T> {
constructor(private value: T) {}
getValue(): T {
return this.value;
}
}In the above syntax:
- <T> : The type parameter that can be replaced with any type.
- constructor : Initializes the class with the specified type.
- getValue() : Ensures type-safe access to the value.
Example 1: Basic Generic Class
A simple generic class that can hold values of different types while ensuring type safety.
class Box<T> {
private content: T;
constructor(content: T) {
this.content = content;
}
getContent(): T {
return this.content;
}
}
const numBox = new Box<number>(100);
console.log("Number Content:", numBox.getContent());
const strBox = new Box<string>("Hello, TypeScript Generics!");
console.log("String Content:", strBox.getContent());
Output:
Number Content: 100
String Content: Hello, TypeScript Generics!In this example:
- The class
Box<T>defines a type parameterTthat represents the type of content. - When creating an instance of
Box, we specify a type (numberorstring). - The class enforces type safety by keeping the same type throughout.
Example 2: Using Generic Constraints in Classes
Constraints in generics restrict the types that can be used as type parameters.
class Box<T extends number> {
private value: T;
constructor(value: T) {
this.value = value;
}
double(): number {
return this.value * 2;
}
}
const numBox = new Box(10);
console.log("Double Value:", numBox.double());
Output:
Double Value: 20In this example:
T extends numberensures only numeric values are allowed.- If we try to pass a non-number, TypeScript shows a compile-time error.
Example 3: Multiple Type Parameters in Generic Classes
A generic class can accept multiple type parameters to manage different data types simultaneously.
class Pair<K, V> {
private key: K;
private value: V;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
}
getKey(): K {
return this.key;
}
getValue(): V {
return this.value;
}
}
const userPair = new Pair<number, string>(1, "John Doe");
console.log("Key:", userPair.getKey(), "Value:", userPair.getValue());
Output:
Key: 1 Value: John DoeIn this example:
- The class
Pair<K, V>defines two type parameters,KandV. - This provides flexibility to store and retrieve values of two different types.
- Here, we store a
(number, string)pair.
Example 4: Generic Classes with Static Properties
Static properties belong to the class itself and cannot be generic, but they can be used alongside generic class instances.
class Counter<T> {
private value: T;
static count: number = 0;
constructor(value: T) {
this.value = value;
Counter.count++;
}
static getCount(): number {
return Counter.count;
}
}
const obj1 = new Counter<number>(10);
const obj2 = new Counter<string>("Hello");
console.log("Total Instances Created:", Counter.getCount());
Output:
Total Instances Created: 2In this example:
Counter<T>is a generic class, butcountis a static property shared across all instances.- The static method
getCount()tracks the total number of instances created. - Static properties cannot be generic; they must have a concrete type.
Types of Generic Classes
TypeScript supports different variations of generic classes based on their structure and constraints.
1. Single Type Parameter Generic Class
A generic class that works with a single type parameter. It is the most commonly used generic class type, allowing flexibility while ensuring type safety. It provides better code maintainability by reducing redundant type-specific implementations.
class Container<T> {
private item: T;
constructor(item: T) {
this.item = item;
}
getItem(): T {
return this.item;
}
}
2. Multiple Type Parameters Generic Class
A class that supports multiple type parameters, allowing it to store two different types. This is useful when dealing with key-value pairs, mapping relationships, or associating different data types together in a structured manner.
class KeyValue<K, V> {
constructor(public key: K, public value: V) { }
}
3. Bounded Generic Class
A class that restricts type parameters using constraints to ensure that only specific types are allowed.
class Num<T extends number> {
private num: T;
constructor(num: T) {
this.num = num;
}
double(): number {
return this.num * 2;
}
}
4. Generic Class with Default Type
A generic class that assigns a default type when none is specified. This helps ensure a default behavior without requiring explicit type declaration every time.
class Default<T = string> {
private item: T;
constructor(item: T) {
this.item = item;
}
getItem(): T {
return this.item;
}
}
5. Recursive Generic Class
A generic class that references itself, useful in implementing linked lists or tree structures. It allows nodes to link to other nodes of the same type, enabling dynamic data structures.
class Processor<T = any> {
process(item: T): void {
console.log("Processing:", item);
}
}