React Hooks Typing
Type-safe state management.
Typing State
For simple state, TypeScript infers the type automatically: const [count, setCount] = useState(0) infers number.
But for complex state, especially when using useReducer, you must explicitly define the shapes of all possible state transitions.
The Optional Payload Trap
A common mistake when typing a reducer action is type Action = { type: string, payload?: any }.
This is dangerous! It allows you to dispatch { type: 'INCREMENT', payload: 'foo' }, even though incrementing shouldn't take a payload, and certainly not a string.
The correct pattern is a **Discriminated Union**.
Syntax
tsx
// 1. Define exact action shapes
type Action =
| { type: "INCREMENT" }
| { type: "DECREMENT" }
| { type: "SET_VALUE"; payload: number };
// 2. Type the Reducer
function reducer(state: number, action: Action) {
switch (action.type) {
case "INCREMENT": return state + 1;
case "SET_VALUE": return action.payload; // Type-safe!
}
}
Try it
Select an action type to dispatch, and watch how the Discriminated Union instantly restricts whether a payload is allowed, required, or forbidden.
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
- Simple
useState(0)is auto-inferred. - Complex
useState<User | null>(null)requires a generic. - Always type
useReduceractions using a Discriminated Union. - Always initialize DOM
useRefwithnull.
Done with this concept?
Mark it complete to track your progress. No login needed.