typescriptjavascriptprogrammingtutorial
Mastering TypeScript Generics: A Practical Guide
Stop writing repetitive code. Learn how TypeScript generics can make your code more reusable, type-safe, and elegant with real-world examples.
December 28, 20253 min read
Mastering TypeScript Generics: A Practical Guide
Generics are one of TypeScript's most powerful features, yet many developers avoid them because they seem complex. Let's change that.
What Are Generics?
Generics let you write code that works with multiple types while maintaining type safety. Think of them as type variables.
typescript
// Without generics - you'd need multiple functions
function getFirstString(arr: string[]): string | undefined {
return arr[0];
}
function getFirstNumber(arr: number[]): number | undefined {
return arr[0];
}
// With generics - one function handles all types
function getFirst<T>(arr: T[]): T | undefined {
return arr[0];
}
// TypeScript infers the type
const first = getFirst([1, 2, 3]); // type: number | undefined
const name = getFirst(["a", "b"]); // type: string | undefinedReal-World Examples
1. API Response Wrapper
typescript
interface ApiResponse<T> {
data: T;
status: number;
message: string;
timestamp: Date;
}
async function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
const data = await response.json();
return {
data: data as T,
status: response.status,
message: response.statusText,
timestamp: new Date()
};
}
// Usage - fully typed!
interface User {
id: string;
name: string;
email: string;
}
const { data: user } = await fetchApi<User>("/api/user/123");
console.log(user.name); // TypeScript knows this is a string2. Generic React Components
typescript
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>
{renderItem(item, index)}
</li>
))}
</ul>
);
}
// Usage
<List
items={users}
renderItem={(user) => <span>{user.name}</span>}
keyExtractor={(user) => user.id}
/>3. Generic Constraints
Sometimes you need to restrict what types can be used:
typescript
interface HasId {
id: string | number;
}
function findById<T extends HasId>(items: T[], id: T["id"]): T | undefined {
return items.find(item => item.id === id);
}
// Works with any object that has an id
const user = findById(users, "123");
const product = findById(products, 456);Common Patterns
The keyof Operator
typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Tejas", age: 25 };
const name = getProperty(user, "name"); // type: string
const age = getProperty(user, "age"); // type: numberMapped Types
typescript
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type Partial<T> = {
[K in keyof T]?: T[K];
};
type Required<T> = {
[K in keyof T]-?: T[K];
};Tips for Using Generics
- Start simple - Add generics when you see repetition
- Name meaningfully - Use
Tfor type,Kfor key,Vfor value - Add constraints - Use
extendsto limit acceptable types - Let TypeScript infer - Don't specify types when inference works
Conclusion
Generics aren't scary—they're just variables for types. Start using them in your API calls and utility functions, and you'll wonder how you ever lived without them.