TypeScript for React
How to type component props, event handlers, and hooks correctly. The contracts that prevent silent bugs.
Use with AI
npx skills add sindev08/react-principles-skillsPrinciple
Bugs caught at compile time cost nothing to fix. Bugs caught in production cost everything. TypeScript for React is not about learning the full TypeScript language — it is about writing the right contracts between your components so that mistakes are caught before the code even runs.
Start by typing your component props. If you can describe what a component accepts and returns, the rest of the types follow naturally.
Rules
- check_circleinterface for component propsUse interface to define component props. It is extendable and reads clearly as a contract.
- check_circletype for unions and utilitiesUse type for union types, utility types, and function signatures — things that are not directly 'objects with fields'.
- check_circleNever use anyany disables type checking completely. Use unknown and narrow it with type guards instead.
- check_circlestrict: true in tsconfigStrict mode enables the full set of type checks. Without it, TypeScript catches only the most obvious errors.
Pattern
import type { ReactNode } from 'react'; // ✅ interface for component props interface UserCardProps { name: string; email: string; role: 'admin' | 'editor' | 'viewer'; // union type isActive: boolean; onEdit: (id: string) => void; // typed event handler children?: ReactNode; } // ✅ typed event handler function handleClick(event: React.MouseEvent<HTMLButtonElement>) { event.preventDefault(); } // ✅ typed useState const [count, setCount] = useState<number>(0); // ❌ never do this const fetchUser = async (): Promise<any> => { ... } // ✅ use unknown and narrow const fetchUser = async (): Promise<unknown> => { ... }
Implementation
Version Compatibility
Requires React 19+ and the latest stable versions of all dependencies shown.
Next.js page components receive typed params and searchParams. Always type these explicitly. URL params are always strings — convert to the expected type before use.
// ✅ Typed Next.js page props interface PageProps { params: Promise<{ id: string }>; searchParams: Promise<{ tab?: string }>; } export default async function UserPage({ params }: PageProps) { const { id } = await params; // URL params are always strings — convert to number before passing to the hook return <UserDetail id={Number(id)} />; } // ✅ Typed Server Action async function updateUser( id: string, data: UpdateUserInput ): Promise<{ success: boolean }> { 'use server'; // ... }
View TypeScript config in starter
View the real implementation in react-principles-nextjs