How to type useCallback()?
Basic syntax
const callback = useCallback(() => {
// ...
}, []);useCallback returns a memoized function whose type depends on its body.
TypeScript infers the type automatically if everything is declared explicitly.
1. The simplest option - automatic inference (recommended)
TypeScript infers the type itself when the parameters and return value are specified inside:
const handleClick = useCallback((id: number) => {
console.log(id);
}, []);The type here is:
(id: number) => voidRecommendation: don't duplicate the typing outside if TypeScript can infer it itself.
2. Specifying the type explicitly (for example, for callbacks in props)
Sometimes you need the callback's signature to match the expected type from props or an interface.
type OnSelect = (id: string) => void;
const handleSelect: OnSelect = useCallback((id) => {
console.log(id);
}, []);Or, to avoid writing extra code:
const handleSelect = useCallback<OnSelect>((id) => {
console.log(id);
}, []);We specify the generic
<OnSelect>right onuseCallback. This forces TypeScript to strictly check the function's signature.
3. Fully typing the signature inside useCallback
If you want everything explicit in one place:
const handleChange = useCallback<(event: React.ChangeEvent<HTMLInputElement>) => void>(
(event) => {
console.log(event.target.value);
},
[]
);Now handleChange is strictly typed as ChangeEventHandler<HTMLInputElement>.
4. An example with a return value
const compute = useCallback((a: number, b: number): number => {
return a + b;
}, []);Type:
(a: number, b: number) => numberTypeScript infers the type automatically - there's no need to explicitly write useCallback<number>.
5. Typing alongside React.FC or props
Suppose we have a component that receives a callback in its props:
type Props = {
onSave: (data: string) => void;
};
function Form({ onSave }: Props) {
const handleSubmit = useCallback(() => {
onSave('data');
}, [onSave]);
return <button onClick={handleSubmit}>Save</button>;
}Here handleSubmit will automatically infer the type (event: React.MouseEvent<HTMLButtonElement>) => void,
if you pass it to onClick.
6. Using it with event handlers
React already provides types for events:
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
},
[]
);or with a generic approach:
const handleClick = useCallback<React.MouseEventHandler<HTMLButtonElement>>(
(e) => {
e.preventDefault();
},
[]
);
React.MouseEventHandler<T>=(event: React.MouseEvent<T, MouseEvent>) => void
7. If you need to type a generic function
const identity = useCallback(<T,>(value: T): T => {
return value;
}, []);Usage:
identity(123); // number
identity('hello'); // stringThe generic is preserved - useCallback works correctly with <T,>.
8. Using it together with useMemo
If useCallback returns a complex function, you can explicitly type its signature and pass it to memoization:
type FetchFn = (url: string) => Promise<Response>;
const fetchData = useCallback<FetchFn>(async (url) => {
const res = await fetch(url);
return res;
}, []);9. Common mistakes
Mistake 1: not specifying dependencies
const handle = useCallback(() => console.log(value), []);If value changes, the function will "hang on" to the old value.
Fix it:
const handle = useCallback(() => console.log(value), [value]);Mistake 2: wrong generic order
useCallback<void, [string]>((value) => {}, []); // this doesn't workThis syntax doesn't work. useCallback accepts one generic - the whole function type:
useCallback<(value: string) => void>((value) => {}, []);Summary
| Scenario | How to type it |
|---|---|
| A simple function | Automatically (TS infers it) |
| A defined signature | useCallback<FnType>(...) |
| A callback from props | const cb: Props["onChange"] = useCallback(...) |
| React events | useCallback<React.ChangeEventHandler<HTMLInputElement>> |
| A generic function | useCallback<<T,>(x: T) => T> |
Recommendations
- Don't type it manually if TypeScript can infer the type.
- If the type matters for compatibility with props, use a generic on
useCallback<>(). - For events, use the built-in types from
React.*EventHandler. - For generic functions, don't forget the comma
<T,>.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.