How do you type state with useState()?
Basic cases
javascript
// Type inferred from the literal
const [count, setCount] = useState(0); // number
const [title, setTitle] = useState("Hello"); // string
const [open, setOpen] = useState(false); // booleanWhen you need to specify the type explicitly:
- the initial value is null / undefined;
- complex unions, objects, arrays;
- you want to narrow the type (for example, literal types).
javascript
// Explicit typing with null
const [user, setUser] = useState<User | null>(null);
// Union
type Status = "idle" | "loading" | "success" | "error";
const [status, setStatus] = useState<Status>("idle");
// Literal variants
const [variant, setVariant] = useState<"primary" | "ghost">("primary");Objects and partial updates
javascript
type Form = { name: string; age: number };
const [form, setForm] = useState<Form>({ name: "", age: 0 });
// Update based on the previous state
setForm(prev => ({ ...prev, name: "Tim" }));Tips • The type of prev is inferred automatically. • For multi-step forms and complex logic, consider useReducer.
Arrays and collections
javascript
const [items, setItems] = useState<string[]>([]);
// Adding an item
setItems(prev => [...prev, "new"]);
// Set/Map (mutable!) - create new instances
const [ids, setIds] = useState<Set<string>>(new Set());
setIds(prev => new Set(prev).add("42"));Nullable / Optional done right
javascript
// Better: an explicit union with null
const [el, setEl] = useState<HTMLDivElement | null>(null);
// Same with undefined
const [token, setToken] = useState<string | undefined>(undefined);Don't use "whatever works" via as any: it breaks TS's guarantees.
Lazy initializer (for expensive computations)
javascript
const [big, setBig] = useState<number>(() => heavyCompute());
// The function runs once, on mount.Derived types and "narrow" literals
javascript
// From API types
type User = ApiUser & { fullName: string };
// A narrow literal via a const assertion
const [align, setAlign] = useState<"left" | "center" | "right">("left");
// Or derive it from a settings object:
const ALIGN = ["left", "center", "right"] as const;
type Align = (typeof ALIGN)[number];
const [a, setA] = useState<Align>("left");Request state (discriminated unions)
javascript
type LoadState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: Post[] }
| { status: "error"; error: string };
const [state, setState] = useState<LoadState>({ status: "idle" });
setState({ status: "loading" });
// ...
setState({ status: "success", data });Convenient for exhaustive switch statements without a "default".
Setter types and updater functions
javascript
const [n, setN] = useState(0);
// Type: React.Dispatch<React.SetStateAction<number>>
// You can pass a function that receives prev
setN(prev => prev + 1);Controlled inputs
javascript
const [value, setValue] = useState("");
const onChange = (e: React.ChangeEvent<HTMLInputElement>) =>
setValue(e.target.value);When to specify the generic explicitly
Specify <T> if:
- the initial value is null/undefined;
- arrays/objects/unions;
- you want to "lock in" an exact type (for example, string vs string | undefined).
Quick examples:
javascript
// good
const [data, setData] = useState<Post[] | null>(null);
// good
const [map, setMap] = useState<Map<string, number>>(new Map());
// good
const [mode, setMode] = useState<"view" | "edit">("view");
// bad - too broad, any
const [unknown, setUnknown] = useState<any>(null);Common mistakes and how to avoid them
- Initializing useState(null) without a type -> becomes null, and TS keeps complaining afterward. Fix: useState<User | null>(null)
- Mutating complex structures (Set/Map, objects) -> React won't see the change. Fix: create a new structure.
- State tied to many branches -> a tangled union. Fix: think about useReducer.
Summary
- Trust type inference, but specify
<T>for null/undefined and complex shapes. - For objects and arrays: immutable updates.
- For loading scenarios: discriminated unions.
- Don't be afraid of the lazy initializer and literal types: they make state precise and reliable.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.