Skip to main content

TypeScript for React Devs

A no-fluff guide to typing props, state, events, and async data in React.

Web DevelopmentIntermediate 16 min read 5 lessons
TypeScriptReact

What You'll Learn

  • Type component props with interface and handle optional fields
  • Use generics to build reusable components and hooks
  • Correctly type DOM events, refs, and forms
  • Handle async data with discriminated unions
  • Set up a strict TypeScript config for React projects

Prerequisites

  • JavaScript ES6+ (destructuring, spread, async/await)
  • Basic React (components, hooks, props)
  • TypeScript fundamentals (types, interfaces)

Step-by-Step Guide

  1. Step 1

    1. Typing Component Props

    Define props with an interface, not inline. Use interface over type for props because interfaces are extensible with extends. Mark optional props with ?. For children, use React.ReactNode (accepts anything renderable). For event handlers, use React.MouseEventHandler<HTMLButtonElement> or inline (e: React.MouseEvent<HTMLButtonElement>) => void.

  2. Step 2

    2. useState & Discriminated Unions

    useState<T> infers from the initial value, but be explicit for unions: const [status, setStatus] = useState<'idle'|'loading'|'error'|'success'>('idle'). This creates a discriminated union — switch/case on status gives TypeScript full narrowing. Use a type with shape { status: 'error'; message: string } | { status: 'success'; data: T } to bundle related fields.

  3. Step 3

    3. Typing DOM Events & Forms

    For input onChange: (e: React.ChangeEvent<HTMLInputElement>) => void. For form onSubmit: (e: React.FormEvent<HTMLFormElement>) => void — call e.preventDefault() inside. For refs: const inputRef = useRef<HTMLInputElement>(null) — note the null initial value and the required null check before accessing inputRef.current?.focus().

  4. Step 4

    4. Generic Components & Hooks

    Generic components accept a type parameter: function List<T extends { id: string }>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }). Generic hooks let callers specify the return type: function useFetch<T>(url: string): { data: T | null; loading: boolean; error: string | null }. This makes your utilities composable and type-safe.

  5. Step 5

    5. Strict Config & Common Pitfalls

    Enable strict: true in tsconfig.json — this catches the most bugs. Avoid casting with as unless you have no alternative (use type guards instead). Don't use any; prefer unknown and narrow it. Avoid React.FC for components — it hides the return type and adds implicit children. Use function declarations or const with an explicit return type annotation.

Further Reading & Resources

Ready to put this into practice?

Apply for a real Web Development internship and ship production projects with mentorship.

Related Tutorials