Generic Classes and Tools
Just like functions and interfaces, classes can also be generic. This is highly useful when building data collections, state containers, or data access layers.
1. Creating a Generic Class
Let us build a type-safe DataRepository class that manages an array of items of any type.
class DataRepository<T> {
private data: T[] = [];
public add(item: T): void {
this.data.push(item);
}
public getAll(): T[] {
return this.data;
}
}Instantiating the Class
You can instantiate a generic class by specifying the type parameters in angle brackets, or let TypeScript infer it:
// Explicit type definition
const textRepo = new DataRepository<string>();
textRepo.add("hello");
// Compiler prevents adding numbers to a string repository
// textRepo.add(42); 2. Generic Class Constraints
You can restrict class types using constraints, similar to functions.
interface Identifiable {
id: string;
}
class EntityStore<T extends Identifiable> {
private store = new Map<string, T>();
public save(entity: T): void {
this.store.set(entity.id, entity);
}
public get(id: string): T | undefined {
return this.store.get(id);
}
}3. Generic Utility Helpers
Generics are excellent for writing utility functions that wrap async behavior. For example, a fetch wrapper that casts the response payload:
async function fetchJSON<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error("Network response error");
}
return response.json() as Promise<T>;
}4. Summary
- Classes use
<T>in their declaration header to introduce type parameters. - Generic classes manage state and logic consistently across multiple input types.
- Apply constraints (using
extends) to restrict generic class instances to compatible objects. - Leverage generic return types (like
Promise<T>) to type-wrap asynchronous actions safely.
Published on Last updated: