Як типізувати useCallback()?
Базовий синтаксис
const callback = useCallback(() => {
// ...
}, []);useCallback повертає мемоізовану функцію, тип якої залежить від її тіла.
TypeScript автоматично виведе тип, якщо все оголошено явно.
1. Найпростіший варіант - автотипізація (рекомендується)
TypeScript сам виводить тип функції, якщо параметри і значення, що повертається, вказані всередині:
const handleClick = useCallback((id: number) => {
console.log(id);
}, []);Тип тут:
(id: number) => voidРекомендація: не дублюй типізацію зовні, якщо TypeScript може вивести її сам.
2. Вказання типу явно (наприклад, для колбеків у props)
Іноді потрібно, щоб сигнатура колбека збігалася з очікуваним типом з пропсів або інтерфейсу.
type OnSelect = (id: string) => void;
const handleSelect: OnSelect = useCallback((id) => {
console.log(id);
}, []);Або, щоб не писати зайвого:
const handleSelect = useCallback<OnSelect>((id) => {
console.log(id);
}, []);Вказуємо generic
<OnSelect>прямо уuseCallback. Це змусить TypeScript строго перевіряти сигнатуру функції.
3. Повна типізація сигнатури всередині useCallback
Якщо хочеш все явно в одному місці:
const handleChange = useCallback<(event: React.ChangeEvent<HTMLInputElement>) => void>(
(event) => {
console.log(event.target.value);
},
[]
);Тепер handleChange буде строго типізований як ChangeEventHandler<HTMLInputElement>.
4. Приклад зі значенням, що повертається
const compute = useCallback((a: number, b: number): number => {
return a + b;
}, []);Тип:
(a: number, b: number) => numberTypeScript виведе тип автоматично - явно вказувати useCallback<number> не потрібно.
5. Типізація в парі з React.FC або пропсами
Припустимо, у нас компонент, який приймає колбек у пропсах:
type Props = {
onSave: (data: string) => void;
};
function Form({ onSave }: Props) {
const handleSubmit = useCallback(() => {
onSave('data');
}, [onSave]);
return <button onClick={handleSubmit}>Save</button>;
}Тут handleSubmit виведе тип (event: React.MouseEvent<HTMLButtonElement>) => void автоматично,
якщо ви передасте його в onClick.
6. Використання з event-хендлерами
React вже надає типи для подій:
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
},
[]
);або з generic-підходом:
const handleClick = useCallback<React.MouseEventHandler<HTMLButtonElement>>(
(e) => {
e.preventDefault();
},
[]
);
React.MouseEventHandler<T>=(event: React.MouseEvent<T, MouseEvent>) => void
7. Якщо потрібно типізувати generic-функцію
const identity = useCallback(<T,>(value: T): T => {
return value;
}, []);Використання:
identity(123); // number
identity('hello'); // stringGeneric зберігається - useCallback коректно працює з <T,>.
8. Спільне використання з useMemo
Якщо useCallback повертає складну функцію, можна явно типізувати її сигнатуру і передати в мемоізацію:
type FetchFn = (url: string) => Promise<Response>;
const fetchData = useCallback<FetchFn>(async (url) => {
const res = await fetch(url);
return res;
}, []);9. Часті помилки
Помилка 1: не вказувати залежності
const handle = useCallback(() => console.log(value), []);Якщо value змінюється - функція буде "зависати" на старому значенні.
Виправ:
const handle = useCallback(() => console.log(value), [value]);Помилка 2: неправильний generic-порядок
useCallback<void, [string]>((value) => {}, []); // так не працюєТакий синтаксис не працює. useCallback приймає один generic - тип функції цілком:
useCallback<(value: string) => void>((value) => {}, []);Підсумок
| Сценарій | Як типізувати |
|---|---|
| Проста функція | Автоматично (TS сам виведе) |
| Визначена сигнатура | useCallback<FnType>(...) |
| Callback з пропсів | const cb: Props["onChange"] = useCallback(...) |
| Події React | useCallback<React.ChangeEventHandler<HTMLInputElement>> |
| Generic функція | useCallback<<T,>(x: T) => T> |
Рекомендації
- Не типізуй вручну, якщо TypeScript може вивести тип.
- Якщо тип важливий для сумісності з пропсами - використовуй generic у
useCallback<>(). - Для подій - використовуй вбудовані типи з
React.*EventHandler. - Для generic-функцій - не забувай про кому
<T,>.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.