Skip to Content
FrontendReactIntermediate2.6 TypeScript + React

TypeScript + React 🧠📘

TypeScript tightens React apps by catching type issues during development.

1) Typing Props & State ✅

type ButtonProps = { label: string; onPress?: () => void; variant?: "primary" | "secondary"; }; function Button({ label, onPress, variant = "primary" }: ButtonProps) { const className = variant === "primary" ? "btn-primary" : "btn-secondary"; return ( <button className={className} onClick={onPress}> {label} </button> ); }
  • Use literal unions for variants.
  • Infer state types from initial values, or annotate generics when needed: const [items, setItems] = useState<Array<Todo>>([]);

2) Typing Events 🧠

  • React wraps DOM events: React.ChangeEvent, React.FormEvent, etc.
function SearchInput() { const [value, setValue] = useState(""); function handleChange(event: React.ChangeEvent<HTMLInputElement>) { setValue(event.target.value); } function handleSubmit(event: React.FormEvent<HTMLFormElement>) { event.preventDefault(); console.log(value); } return ( <form onSubmit={handleSubmit}> <input value={value} onChange={handleChange} /> </form> ); }
  • For custom components, type callbacks: onSelect?: (id: string) => void;

3) Generics with Components & Hooks ⚙️

  • Write generic components when handling reusable data shapes.
type ListProps<T> = { items: T[]; renderItem: (item: T) => React.ReactNode; }; function List<T>({ items, renderItem }: ListProps<T>) { return <ul>{items.map((item, index) => <li key={index}>{renderItem(item)}</li>)}</ul>; }
  • Generic hooks capture reusable logic:
function usePrevious<T>(value: T) { const ref = useRef<T>(value); useEffect(() => { ref.current = value; }, [value]); return ref.current; }

4) Discriminated Unions for UI States 🧩

Model UI status as a union to keep rendering logic exhaustive.

type AsyncState<T> = | { status: "idle" } | { status: "loading" } | { status: "success"; data: T } | { status: "error"; error: string }; function DataPanel() { const [state, setState] = useState<AsyncState<User>>({ status: "idle" }); if (state.status === "idle") return <p>Start searching</p>; if (state.status === "loading") return <p>Loading...</p>; if (state.status === "error") return <p>❌ {state.error}</p>; return <ProfileCard user={state.data} />; }
  • switch statements can ensure exhaustive handling (default: never pattern).

Key Takeaways ✅

  • Type props with interfaces/unions; annotate state when inference fails.
  • Use React event typings to access DOM-safe properties.
  • Generics make reusable components and hooks type-safe.
  • Discriminated unions turn UI status flags into explicit branches.

Recap 🔄

TypeScript complements React by catching prop mismatches, ensuring event correctness, and modeling UI states precisely. Lean on interfaces, generics, and unions to keep components predictable and self-documenting.

Last updated on