TypeScript really shines in React when working with component props, events, and hooks like useState
or useEffect
. Here’s how to type them properly so you get type safety and helpful autocompletions.
Typing Component Props
Copy interface Todo {
title: string;
done: boolean;
}
interface TodoItemProps {
todo: Todo;
onToggle: (title: string) => void;
}
const TodoItem: React.FC<TodoItemProps> = ({ todo, onToggle }) => (
<div>
<input
type="checkbox"
checked={todo.done}
onChange={() => onToggle(todo.title)}
/>
{todo.title}
</div>
);
💡 Tip: Try it in your IDE!
With typed props, you’ll get autocompletion and instant feedback — making mistakes harder and coding faster.
Typing useState
Step 1: Create the state type
Copy interface Todo {
title: string;
done: boolean;
}
Step 2: Type the useState hook
Copy // empty initial state
const [todos, setTodos] = useState<Todo[]>([]);
Copy const INITIAL_TODO: Todo[] = [
{
title: "first task",
done: false,
},
{
title: "second task",
done: true,
},
{
title: "third task",
done: true,
},
];
const [todos, setTodos] = useState<Todo[]>(INITIAL_TODO);
Now todos
is typed as an array of Todo
objects, and setTodos
will only accept that type.
Step 3: Using setTodos
Here are three valid and clean ways to use setTodos
:
Copy // 1. Basic usage — straightforward and readable
setTodos([{ title: "new todo", done: false }]);
// 2. Callback form — preferred if referencing previous state or writing multiple lines
setTodos((prevTodos) => {
return [...prevTodos, { title: "new todo", done: false }];
});
// 3. One-liner callback — clean if short
setTodos((prev) => [...prev, { title: "new todo", done: false }]);
React batches updates — using the callback
ensures you’re working with the most recent state , especially inside useEffect
or async operations.
Typing Events
React’s synthetic events are strongly typed too. Here’s an example with an input change handler:
Copy const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
Typing Custom Hooks
If you create your own hooks, type the input and output clearly:
Copy function useTodoList(): [Todo[], (todo: Todo) => void] {
const [todos, setTodos] = useState<Todo[]>([]);
const addTodo = (todo: Todo) => setTodos((prev) => [...prev, todo]);
return [todos, addTodo];
}
Copy const [todos, addTodo] = useTodoList();
addTodo({ title: "Learn TS", done: false });