The silent horror of every React developer: event.target.value and TypeScript
Have you ever written an onChange handler for a simple input, and TypeScript suddenly started screaming in red? Sound familiar: "Property 'value' does not exist on type 'EventTarget'". This question on Stack Overflow has gathered tens of thousands of views, and for good reason. It seems like nothing could be simpler — getting a value from an input field? But the devil, as always, is in the details.
In 2025, the TypeScript ecosystem has become even stricter. New compiler versions analyze types better, and old "hacks" have stopped working. If you're writing in React or simply using TypeScript for DOM manipulation, this article is your lifeboat. We'll break down not only how to type onChange, but also why it works this way and not otherwise. Let's go!
Why is event.target.value a pain?
Let's get straight to the point. Here's a typical example that causes an error:
const handleChange = (event: any) => { const value = event.target.value; // Works, but "any" is evil};The problem is that event could be anything. TypeScript doesn't know what element it is or if it has a value property. Moreover, using any kills all the magic of static typing. You lose autocompletion and protection against errors.
The right way is to use specific types. But which ones? Let's figure it out.
Best practices for typing onChange in 2025
1. React: use ChangeEvent
If you're working with React, forget about the native Event. React uses its own synthetic events — SyntheticEvent. For inputs, textareas, and selects, ChangeEvent is ideal.
import React, { ChangeEvent } from 'react';
const MyInput = () => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const value = event.target.value; console.log(value); // value is a string, TypeScript is happy };
return <input type="text" onChange={handleChange} />;};Why does this work? ChangeEvent<HTMLInputElement> is a generic that knows for sure that event.target is an HTMLInputElement. And it has a value property of type string. Everything is logical and type-safe.
2. For textarea and select
A similar approach works for other form elements. Just change the generic:
ChangeEvent<HTMLTextAreaElement>— for<textarea>ChangeEvent<HTMLSelectElement>— for<select>ChangeEvent<HTMLInputElement>— for<input>
const handleTextareaChange = (event: ChangeEvent<HTMLTextAreaElement>) => { const text = event.target.value; // string};
const handleSelectChange = (event: ChangeEvent<HTMLSelectElement>) => { const selectedValue = event.target.value; // string};3. Vanilla TypeScript (without React): Event + type assertion
If you're not using React and are working directly with the DOM, things are a bit different. The native Event doesn't know about the value property. You need to cast event.target to the correct type.
const inputElement = document.getElementById('my-input') as HTMLInputElement;
inputElement.addEventListener('change', (event: Event) => { const target = event.target as HTMLInputElement; const value = target.value; console.log(value);});Important: Use type assertions (as HTMLInputElement) only when you are 100% sure of the element's type. Otherwise, you risk getting a runtime error.