← Back to TypeScript Course | Chapter 14: TypeScript with React | Lesson 3 of 8

Typing State

React state should have a predictable type so that updates remain safe. TypeScript can infer many state types automatically, while generic type parameters are useful for unions and complex initial values.
Syntax
typescript
const [state, setState] = useState<StateType>(initialValue);

Basic useState Typing

Basic useState typing is inferred automatically from the initial value you pass in, so useState(0) already gives you a state variable typed as number without needing any manual annotation.

Example: Basic useState Typing

typescript
import React, { useState } from "react";

function Counter(): React.JSX.Element {
	const [count, setCount] = useState(0);
	return <p>{count}</p>;
}

export default Counter;

Explicit State Types

An explicit type argument to useState<T>() is needed when the initial value doesn't fully describe the type you actually want — for example when the state can later hold one of several different shapes.

Example: Explicit State Types

typescript
import React, { useState } from "react";

type Status = "idle" | "loading" | "done";

function Loader(): React.JSX.Element {
	const [status, setStatus] = useState<Status>("idle");
	return <p>{status}</p>;
}

export default Loader;

Nullable State

Nullable state is typed explicitly as useState<User | null>(null), which forces every place that reads the state to first check it isn't null before accessing a property on the expected object.

Example: Nullable State

typescript
import React, { useState } from "react";

interface User {
	name: string;
}

function Profile(): React.JSX.Element {
	const [user, setUser] = useState<User | null>(null);
	return <p>{user ? user.name : "No user"}</p>;
}

export default Profile;

Array State

Array state benefits from an explicit type argument like useState<Item[]>([]), since an empty array alone gives TypeScript no information about what type of items the array is meant to eventually hold.

Example: Array State

typescript
import React, { useState } from "react";

interface Item {
	id: number;
	label: string;
}

function List(): React.JSX.Element {
	const [items, setItems] = useState<Item[]>([]);
	return <p>{items.length} items</p>;
}

export default List;

State with Functional Updates

Functional updates — setState(prev => ...) — automatically type the prev parameter as the state's current type, letting you safely compute a new value from the previous one without re-declaring the type by hand.

Example: State with Functional Updates

typescript
import React, { useState } from "react";

function Counter(): React.JSX.Element {
	const [count, setCount] = useState(0);
	const increment = () => setCount((prev) => prev + 1);
	return <button onClick={increment}>{count}</button>;
}

export default Counter;
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Writing useState([]) and getting never[], so pushing an item errors; use useState<Item[]>([]).
  2. Starting with useState(null) and later setting a user object, when the state type is inferred as null and needs useState<User | null>(null).
  3. Mutating state directly, like items.push(x), instead of calling setItems([...items, x]).
🔒

Chapter Quiz — Complete all 8 topics to unlock

0/8 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.