Deep Dive into TypeScript Generics
Generics are the secret sauce that gives TypeScript its “type-safe” power while keeping code flexible. In this article we’ll break down what generics are, why you should use them, and how to avoid the most common pitfalls.
What Are Generics?
A generic type is a placeholder that will be replaced with an actual type when you use the function, class, or interface. Think of it like a template that can be instantiated with any type.
Advantages
- ✅ Type safety – Catch errors at compile time.
- 🛠️ Reusable code – Write once, use for many types.
- 🚀 Better IDE support – Autocomplete & type hints work seamlessly.
Basic Syntax
// Generic function
function identity<T>(arg: T): T {
return arg;
}
const num = identity(42); // T inferred as number
const str = identity('hello'); // T inferred as string
Common Patterns
Generic Interfaces
interface Box<T> {
value: T;
}
const numberBox: Box<number> = { value: 123 };
const stringBox: Box<string> = { value: 'hello' };
Generic Classes
class Queue<T> {
private data: T[] = [];
enqueue(item: T): void { this.data.push(item); }
dequeue(): T | undefined { return this.data.shift(); }
}
Advanced Use‑Cases
When you need to enforce relationships between types, use generic constraints:
interface Lengthwise { length: number; }
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
logLength('hello'); // OK
logLength([1, 2, 3]); // OK
// logLength(42); // ❌ TypeScript error
Common Pitfalls
- 🔍 Over‑constraining generics – Don’t restrict too tightly, it defeats the purpose.
- 💬 Using 'any' – It erases the benefit of generics.
- 👀 Confusing type inference – Explicit type arguments can help readability.
Real‑World Example: A Generic API Client
Imagine you’re building a wrapper around a REST API. Each endpoint returns a different shape of data; generics let you describe that without duplicating code.
async function fetchApi<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json() as Promise<T>;
}
// Usage:
interface User { id: number; name: string; }
const user = await fetchApi<User>('/api/user/1');
console.log(user.name);
With generics you get strong typing for free – no manual casting or runtime checks.
Conclusion
Generics turn TypeScript into a language that’s both safe and flexible. Mastering them is a game‑changer for building scalable applications.