Apply when writing reusable functions, components, or utilities that work with multiple types while maintaining type safety.
Apply when writing reusable functions, components, or utilities that work with multiple types while maintaining type safety.
// Source: https://www.typescriptlang.org/docs/handbook/2/generics.html
function first<T>(array: T[]): T | undefined {
return array[0];
}
const num = first([1, 2, 3]); // number | undefined
const str = first(['a', 'b']); // string | undefined
// Source: https://www.typescriptlang.org/docs/handbook/2/generics.html
interface HasId {
id: string;
}
function findById<T extends HasId>(items: T[], id: string): T | undefined {
return items.find(item => item.id === id);
}
// Works with any object that has 'id'
const user = findById(users, '123');
const post = findById(posts, '456');
// Source: https://www.typescriptlang.org/docs/handbook/2/generics.html
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map(item => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
}
// Usage with full type inference
<List
items={users}
renderItem={(user) => user.name} // user is typed as User
keyExtractor={(user) => user.id}
/>
// Source: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html
type ApiResponse<T> = T extends undefined
? { success: true }
: { success: true; data: T };
// Result: { success: true }
type VoidResponse = ApiResponse<undefined>;
// Result: { success: true; data: User }
type UserResponse = ApiResponse<User>;
// Source: https://www.typescriptlang.org/docs/handbook/2/generics.html
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
// T = User, U = string
const names = map(users, user => user.name);
// Merge objects
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
// Source: https://www.typescriptlang.org/docs/handbook/2/generics.html
interface PaginatedResult<T = unknown> {
data: T[];
page: number;
total: number;
}
// Uses default
const result: PaginatedResult = { data: [], page: 1, total: 0 };
// Explicit type
const users: PaginatedResult<User> = { data: [], page: 1, total: 0 };
// Source: https://www.typescriptlang.org/docs/handbook/utility-types.html
// Blocks inference to ensure parameter matches previously inferred type
function createStreetLight<C extends string>(
colors: C[],
defaultColor?: NoInfer<C>
) {
return { colors, defaultColor };
}
createStreetLight(['red', 'yellow', 'green'], 'red'); // OK
createStreetLight(['red', 'yellow', 'green'], 'blue'); // Error
extends to limit valid typesany instead of generic - Generics preserve type info