When I first started using TypeScript with React, I thought it would just add extra typing overhead. Boy, was I wrong. TypeScript has saved me countless hours by catching bugs before they reach production, and it's made my codebase infinitely more maintainable. But using TypeScript effectively in React requires understanding some patterns and best practices that aren't always obvious.
TypeScript brings compile-time type checking to React, which means you catch errors while writing code, not when users report bugs. It also provides incredible IDE support—autocomplete, refactoring, and navigation all work better when TypeScript understands your code structure. But to get these benefits, you need to type your components, props, hooks, and event handlers correctly.
Why TypeScript with React?
TypeScript with React provides:
- Type safety - Catch errors at compile time
- Better IDE support - Autocomplete, IntelliSense, and refactoring
- Self-documenting code - Types serve as documentation
- Easier refactoring - TypeScript catches breaking changes
- Better collaboration - Types help team members understand code
- Runtime error prevention - Catch bugs before they reach production
Type Definitions for Props
Define component prop types using interfaces or types:
type Product = {
id: string | number;
name: string;
price: number;
stock: number;
categoryId: number;
categoryName?: string;
};
interface ProductCardProps {
product: Product;
onEdit?: (id: string | number) => void;
onDelete?: (id: string | number) => void;
showActions?: boolean;
}
function ProductCard({
product,
onEdit,
onDelete,
showActions = true
}: ProductCardProps) {
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>${product.price}</p>
<p>Stock: {product.stock}</p>
{showActions && (
<div>
{onEdit && <button onClick={() => onEdit(product.id)}>Edit</button>}
{onDelete && <button onClick={() => onDelete(product.id)}>Delete</button>}
</div>
)}
</div>
);
}
Typing React Hooks
useState Hook
Type useState explicitly or let TypeScript infer:
// Explicit typing
const [name, setName] = useState<string>("");
const [count, setCount] = useState<number>(0);
const [products, setProducts] = useState<Product[]>([]);
// Type inference
const [isLoading, setIsLoading] = useState(false); // boolean
const [user, setUser] = useState<User | null>(null); // Better
Custom Hooks
Create typed custom hooks:
type UseProductsReturn = {
products: Product[];
isLoading: boolean;
isError: boolean;
error: any;
refetch: () => void;
};
function useProducts(): UseProductsReturn {
const { data, isLoading, isError, error, refetch } = useGetProductsQuery({});
return {
products: data?.data || [],
isLoading,
isError,
error,
refetch,
};
}
Event Handlers
Type event handlers correctly:
// Form submit handler
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// Handle submission
};
// Button click handler
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
// Handle click
};
// Input change handler
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
Best Practices
- Use interfaces for component props
- Type useState explicitly when needed
- Create typed custom hooks
- Type event handlers correctly
- Use generics for reusable components
- Leverage type inference when possible
📖 Read the Complete Guide
This is just a brief overview! The complete guide on my blog includes:
- ✅ Type vs Interface - When to use each
- ✅ Generics in React - Reusable typed components
- ✅ Context Types - Typing React Context
- ✅ Ref Types - Typing useRef and refs
- ✅ Higher-Order Components - Typing HOCs
- ✅ Advanced Patterns - Utility types, conditional types
- ✅ Real-world examples from production applications
👉 Read the full article with all code examples here
What's your experience with TypeScript and React? Share your tips in the comments! 🚀
For more React guides, check out my blog covering React Router, React Hook Form, Redux Toolkit, and more.
Top comments (0)