Типизация события onChange в React и TypeScript: как получить event.target.value
Правильная типизация события onChange в React + TypeScript
Ваша проблема очень распространена среди разработчиков, переходящих на TypeScript в React. Давайте разберем её подробно.
Основная причина путаницы в том, что в React существует несколько типов событий, и каждый из них предназначен для своей цели. Для обработки изменения значения в <input>, <textarea> или <select> правильным типом будет:
React.ChangeEvent<HTMLInputElement>
Почему именно ChangeEvent, а не FormEvent или любой другой? Потому что onChange в React — это синтетическое событие, которое специально создано для отслеживания изменений в элементах формы. Тип React.ChangeEvent является наиболее точным для этого случая.
Правильный код с типизацией
import React, { useState } from 'react';
const MyForm: React.FC = () => {
const [value, setValue] = useState('');
// ✅ Правильная типизация
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
};
Вариации для разных элементов формы
В зависимости от элемента, который вы используете, меняется только дженерик (generic parameter) внутри угловых скобок:
- Для <input> —
React.ChangeEvent<HTMLInputElement> - Для <textarea> —
React.ChangeEvent<HTMLTextAreaElement> - Для <select> —
React.ChangeEvent<HTMLSelectElement>
Пример с несколькими полями
import React, { useState } from 'react';
interface FormData {
name: string;
bio: string;
country: string;
}
const MyForm: React.FC = () => {
const [formData, setFormData] = useState<FormData>({
name: '',
bio: '',
country: ''
});
// Универсальный обработчик
const handleChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
) => {
const { name, value } = event.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<form>
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<textarea
name="bio"
value={formData.bio}
onChange={handleChange}
/>
<select
name="country"
value={formData.country}
onChange={handleChange}
>
<option value="">Выберите</option>
<option value="ru">Россия</option>
<option value="us">США</option>
</select>
</form>
);
};
Почему не стоит использовать event: any или Event
event: any— отключает всю типовую безопасность TypeScript. Вы не получите автодополнения дляevent.target.valueи не увидите ошибки, если случайно обратитесь к несуществующему свойству.Event— это базовый тип DOM-события. У него нет свойстваtargetв том виде, в котором оно нужно (сvalue). React использует синтетические события, которые являются оберткой над нативными.React.FormEvent— этот тип предназначен для событияonSubmitу формы, а не дляonChangeу инпута.
Дополнительный совет: извлечение типа в отдельную переменную
Если вы используете один и тот же тип события многократно, можно вынести его в отдельный тип для читаемости:
type InputChangeEvent = React.ChangeEvent<HTMLInputElement>;
const handleChange = (event: InputChangeEvent) => {
setValue(event.target.value);
};
Надеюсь, теперь стало понятнее! Главное правило: для onChange всегда используйте React.ChangeEvent с соответствующим HTML-элементом в дженерике.
Альтернативный подход: использование типов из @types/react
Если вы не хотите каждый раз вручную указывать дженерик или у вас сложная форма с множеством разных элементов, можно воспользоваться встроенными типами React более высокого уровня.
Например, тип React.ChangeEventHandler<HTMLInputElement> — это готовый тип для функции-обработчика:
import React, { useState } from 'react';
const MyForm: React.FC = () => {
const [value, setValue] = useState('');
// Используем ChangeEventHandler вместо ChangeEvent
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
setValue(event.target.value);
};
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
};
Этот подход особенно удобен, когда вы передаете обработчик как пропс или используете его в хуках. Он делает код более декларативным и сокращает количество повторяющегося текста.
Также можно создать свой кастомный тип для часто используемых комбинаций:
type InputHandler = React.ChangeEventHandler<HTMLInputElement>;
type TextareaHandler = React.ChangeEventHandler<HTMLTextAreaElement>;
const handleInputChange: InputHandler = (e) => {
console.log(e.target.value);
};
const handleTextareaChange: TextareaHandler = (e) => {
console.log(e.target.value);
};
Оба подхода (с ChangeEvent и ChangeEventHandler) абсолютно корректны. Выбирайте тот, который вам удобнее в конкретной ситуации. Главное — не используйте any!
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Функциональное программирование в C#: практическое руководство