Level up your TypeScript skills with these advanced patterns for React development.
Generic Components
Create reusable, type-safe components:
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) => <UserCard user={user} />}
keyExtractor={(user) => user.id}
/>;
Discriminated Unions for State
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
function DataDisplay<T>({ state }: { state: AsyncState<T> }) {
switch (state.status) {
case 'idle':
return <div>Click to load</div>;
case 'loading':
return <Spinner />;
case 'success':
return <div>{state.data}</div>; // TypeScript knows data exists!
case 'error':
return <Error message={state.error.message} />;
}
}
Type Guards
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' && obj !== null && 'id' in obj && 'name' in obj
);
}
// Usage
const data = await fetchData();
if (isUser(data)) {
// TypeScript knows data is User here
console.log(data.name);
}
Utility Types for Props
// Extract props from existing component
type ButtonProps = React.ComponentProps<typeof Button>;
// Make all props optional
type PartialUser = Partial<User>;
// Pick specific props
type UserPreview = Pick<User, 'id' | 'name' | 'avatar'>;
// Omit props
type UserWithoutPassword = Omit<User, 'password'>;
Conditional Types
type APIResponse<T> = T extends { id: string } ? T & { createdAt: Date } : T;
type User = APIResponse<{ id: string; name: string }>;
// Result: { id: string; name: string; createdAt: Date }
Master these patterns to write type-safe, maintainable React applications.
Posted onreactwith tags: