Типизация событий React в TypeScript: как правильно указывать типы для обработчиков?
Основные типы событий React в TypeScript
В React все события обёрнуты в синтетические обёртки SyntheticEvent, которые имеют свои типовые дженерики. Главное правило: тип события зависит от элемента, к которому оно привязано, и от типа самого события. Универсального типа "на все случаи" нет, но все они наследуются от React.SyntheticEvent.
1. Самые частые события
Вот таблица соответствий для популярных элементов и событий:
- onChange для
<input>,<select>,<textarea>→React.ChangeEvent<HTMLInputElement>(или соответствующий элемент) - onSubmit для
<form>→React.FormEvent<HTMLFormElement> - onClick для
<button>,<div>→React.MouseEvent<HTMLButtonElement> - onKeyDown →
React.KeyboardEvent<HTMLInputElement> - onFocus / onBlur →
React.FocusEvent<HTMLInputElement>
2. Пример с формой
import React, { useState } from 'react';
function MyForm() {
const [value, setValue] = useState('');
// Тип события: ChangeEvent для HTMLInputElement
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
// Тип события: FormEvent для HTMLFormElement
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Submitted:', value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">Send</button>
</form>
);
}
3. Типизация пропсов-обработчиков
Когда вы передаёте функцию-обработчик как пропс, используйте сигнатуру с конкретным типом события:
interface InputProps {
onInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
value: string;
}
function CustomInput({ onInputChange, value }: InputProps) {
return <input value={value} onChange={onInputChange} />;
}
// Использование:
<CustomInput
value={text}
onInputChange={(e) => setText(e.target.value)}
/>
4. Как не ошибиться с выбором типа
Есть простой лайфхак: наведите курсор на событие в JSX — IDE покажет ожидаемый тип. Например, если написать onChange={(e) => ...} и навести на e, TypeScript подскажет точный тип. Также можно использовать обобщённый тип React.ChangeEvent<HTMLElement>, но это менее безопасно — вы потеряете автодополнение для свойств вроде e.target.value (оно есть только у HTMLInputElement).
5. Для кастомных событий
Если вы создаёте свои события (например, через EventEmitter или CustomEvent), используйте обычные типы TypeScript:
interface MyCustomEvent {
detail: { userId: number };
}
function handleCustom(e: CustomEvent<MyCustomEvent>) {
console.log(e.detail.userId);
}
В React для работы с DOM-событиями всегда используйте типы из пространства React. Запомните шаблон: React.[ТипСобытия]Event<[HTMLЭлемент]>. Это покрывает 90% случаев.
Упрощённый подход: использование обобщённых типов
Если вы не хотите каждый раз вспоминать конкретный HTML-элемент, можно использовать более общие типы. Например, React.ChangeEvent<HTMLElement> или React.MouseEvent<HTMLElement>. Однако помните, что HTMLElement — это самый базовый интерфейс, и у него нет специфических свойств вроде value или checked. Для доступа к ним придётся делать приведение типа:
const handleChange = (e: React.ChangeEvent<HTMLElement>) => {
const target = e.target as HTMLInputElement;
setValue(target.value);
};
Это работает, но менее безопасно — вы теряете проверку типов на этапе компиляции. Лучше использовать ChangeEvent<HTMLInputElement> напрямую.
Ещё один приём — вынести тип в отдельный дженерик, если у вас много похожих обработчиков:
type InputHandler = (e: React.ChangeEvent<HTMLInputElement>) => void;
const handleChange: InputHandler = (e) => {
setValue(e.target.value);
};
Это сокращает код и делает его более читаемым, особенно в больших компонентах.
Ответов пока нет.
Войдите чтобы оставить ответ