Let's think about this for a second
In this final stage of the project, we'll use generics to refactor the code into something more flexible. Instead of a TaskManager that only works with the Task type, we'll build a generic Repository<T> class that can store any type. This shows how real-world projects can cut down on code duplication. We'll also add a function that uses a tuple type to return summary statistics (total count, done count). Once this stage is done, the whole project will be complete across three layers: setup, features, and polish.
Let's build it
Create a generic class called Repository<T> and implement the methods private items: T[] = [], add(item: T): void, getAll(): T[], and find(predicate: (item: T) => boolean): T | undefined. Rewrite the TaskManager class to use Repository<Task> internally (as composition). Add a function getStats(): [number, number] that returns a tuple in the form [total, doneCount]. Finally, add error handling so that when updateStatus() is called with an id that isn't found, it prints an error message with console.error(), then run the project as a demo.
Sample code
class Repository<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
find(predicate: (item: T) => boolean): T | undefined {
return this.items.find(predicate);
}
}
function getStats(repo: Repository<Task>): [number, number] {
const all = repo.getAll();
const total = all.length;
const doneCount = all.filter((t) => t.status === TaskStatus.Done).length;
return [total, doneCount];
}
const taskRepo = new Repository<Task>();
taskRepo.add({ id: 1, title: "Learn generics", status: TaskStatus.Done, priority: "high" });
taskRepo.add({ id: 2, title: "Write tests", status: TaskStatus.Todo, priority: "medium" });
const [total, done] = getStats(taskRepo);
console.log(`Total: ${total}, Done: ${done}`);
const missing = taskRepo.find((t) => t.id === 99);
if (!missing) {
console.error("Task not found: id 99");
}The console shows "Total: 2, Done: 1", plus an error message since id 99 can't be found.5-minute try it yourself
Try reusing the Repository<T> class for a string[] case too (e.g. a tags list), and confirm that a single generic class can serve two cases with different data types.
A quick word of caution
In larger projects, keep in mind that using a generic class through composition (adding it as a property inside another class) tends to be more flexible and easier to test than using inheritance.