Question
Typing React onChange Event.target.value in TypeScript
Question
In a React and TypeScript application, I am using this code:
onChange={(e) => {
data.motto = (e.target as any).value;
}}
How can I type this correctly so I do not need to bypass the type system with any?
Here is the component setup:
export interface InputProps extends React.HTMLProps<Input> {
// ...
}
export class Input extends React.Component<InputProps, {}> {
}
I tried adding something like target: { value: string }, but then I got this error:
Interface 'InputProps' incorrectly extends interface 'HTMLProps<Input>'.
Types of property 'target' are incompatible.
Type '{ value: string; }' is not assignable to type 'string'.
What is the proper TypeScript way to type an input onChange handler so event.target.value or event.currentTarget.value is safely available?
Short Answer
By the end of this page, you will understand how React types onChange events in TypeScript, why any is unnecessary here, and how to correctly type reusable input component props. You will also learn the difference between target and currentTarget, and how to write safer event handlers for form inputs.
Concept
In React with TypeScript, form events already have built-in types. You usually do not add a custom target property to your props interface. Instead, you type the event handler itself.
For an HTML <input>, the correct event type is usually:
React.ChangeEvent<HTMLInputElement>
That means TypeScript knows the event came from an HTMLInputElement, so properties like .value, .checked, and .name are available.
A common beginner mistake is trying to modify the props interface to redefine event internals such as target. That does not work because target is part of the event object, not your component's props shape.
Another important detail: in React, currentTarget is often safer to use than target.
targetis the element that originally triggered the event.currentTargetis the element the handler is attached to.
For typed form handlers, this is the common pattern:
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.currentTarget.value);
};
This matters in real programming because form handling is everywhere:
- search boxes
- login forms
- settings pages
- filters
- admin dashboards
- controlled inputs in React state
If you type events correctly, TypeScript can catch mistakes early and provide autocomplete for input-specific properties.
Mental Model
Think of a React event as a labeled package.
- The event type tells you what kind of package it is.
- The generic type like
HTMLInputElementtells you which element sent it. - Once the label is correct, TypeScript knows what is inside.
So instead of saying, "Trust me, this package contains a value" with as any, you tell TypeScript, "This is a change event from an input element." Then TypeScript can safely allow value.
Another way to think about it:
propsdescribe what your component accepts.eventdescribes what happened during user interaction.
Do not try to store event typing inside props fields like target. Type the event where it is used.
Syntax and Examples
Basic typed onChange
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.currentTarget.value;
console.log(value);
};
<input onChange={handleChange} />
Here, TypeScript knows:
- this is a change event
- it comes from an
HTMLInputElement valueis a string
Inline handler
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
data.motto = e.currentTarget.value;
}}
/>
Better reusable props typing
If you are building a custom Input component, use input element props rather than HTMLProps<Input> where Input is your React class.
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
}
class Input extends React.Component<InputProps> {
render() {
return <input {...this.props} />;
}
}
This works because React.InputHTMLAttributes<HTMLInputElement> describes the props of a real HTML input element.
Example with state
import React from "react";
export default function ProfileForm() {
const [motto, setMotto] = React.useState("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setMotto(e.currentTarget.value);
};
return <input value={motto} onChange={handleChange} />;
}
This is the most common React pattern: a controlled input tied to state.
Step by Step Execution
Consider this code:
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.currentTarget.value;
console.log(value);
};
Step by step:
- The user types into the input.
- React fires an
onChangeevent. - The handler receives
e. - TypeScript sees that
eis aReact.ChangeEvent<HTMLInputElement>. - Because the element type is
HTMLInputElement,e.currentTarget.valueis known to be a string. - The string is stored in
value. - You can now assign it to state, an object field, or send it to validation logic.
Example in context:
function App() {
const [name, setName] = React.useState("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const nextValue = e.currentTarget.value;
setName(nextValue);
};
return <input value={name} onChange={handleChange} />;
}
Execution trace when the user types A:
- Initial state:
name = "" - User types
A onChangerunse.currentTarget.value === "A"setName("A")- React re-renders
- Input now displays
Afrom state
Real World Use Cases
Correctly typed input change handlers are used in many real applications:
Forms
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEmail(e.currentTarget.value);
};
Used in:
- sign-up forms
- password reset forms
- checkout forms
Search and filtering
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.currentTarget.value);
};
Used in:
- product search
- table filtering
- admin dashboards
Validation
const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.currentTarget.value;
setUsername(value);
setTooShort(value.length < 3);
};
Used in:
- live validation
- character limits
- required-field checks
Reusable UI components
A shared Input component often passes the typed onChange down to a real HTML input.
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
}
Real Codebase Usage
In real projects, developers usually rely on built-in React prop types instead of redefining DOM event shapes manually.
Common pattern: reuse native input props
type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
label?: string;
};
This gives your component support for standard props such as:
valuedefaultValueonChangeplaceholderdisabledname
Common pattern: typed handler passed as prop
interface InputProps {
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
This is useful when you want strict control over which props are allowed.
Guard clauses in handlers
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.currentTarget.value;
if (value.length > 100) return;
setMotto(value);
};
This prevents invalid state updates early.
Validation and transformation
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.currentTarget.value.trimStart();
setMotto(value);
};
Controlled components
Most production React forms use controlled inputs:
<input value={motto} onChange={handleChange} />
This makes input data predictable and easier to validate, submit, and test.
Common Mistakes
1. Using any
Broken:
onChange={(e) => data.motto = (e.target as any).value}
Why it is a problem:
- disables type safety
- hides mistakes
- loses autocomplete
Better:
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
data.motto = e.currentTarget.value;
}}
2. Typing the wrong thing
Broken:
interface InputProps extends React.HTMLProps<Input> {
target: { value: string };
}
Why it is wrong:
targetbelongs to the event object, not the component propsInputhere is your React component class, not the DOM<input>element
Better:
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
}
3. Using the component class instead of the DOM element type
Broken:
React.HTMLProps<Input>
Why it is wrong:
Inputis a React component- HTML attribute types should use DOM element types like
HTMLInputElement
Better:
React.InputHTMLAttributes<HTMLInputElement>
4. Confusing target and currentTarget
This may work sometimes:
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
But this is often clearer and safer:
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.currentTarget.value);
};
5. Forgetting that different form elements have different types
Examples:
- text input:
HTMLInputElement - textarea:
HTMLTextAreaElement - select:
HTMLSelectElement
Correct examples:
const onTextAreaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
console.log(e.currentTarget.value);
};
const onSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
console.log(e.currentTarget.value);
};
Comparisons
| Concept | Best Use | Example Type |
|---|---|---|
React.ChangeEvent<HTMLInputElement> | Typing input change events | Text inputs, checkboxes |
React.ChangeEvent<HTMLTextAreaElement> | Typing textarea changes | Multi-line text |
React.ChangeEvent<HTMLSelectElement> | Typing select changes | Dropdowns |
React.InputHTMLAttributes<HTMLInputElement> | Reusing native input props in a custom component | Wrapper input component |
React.HTMLProps<HTMLInputElement> | Broader HTML props typing | Less specific alternative |
target vs currentTarget
| Property | Meaning | Recommended for typed handlers? |
|---|---|---|
target | The original element that triggered the event | Sometimes |
currentTarget | The element the handler is attached to | Usually yes |
any vs proper typing
| Approach | Result |
|---|---|
(e.target as any).value | Works, but removes safety |
e: React.ChangeEvent<HTMLInputElement> | Safe, explicit, autocomplete-friendly |
Cheat Sheet
// Typed input change event
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.currentTarget.value;
};
// Custom input props
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
}
// Explicit onChange prop typing
interface InputProps {
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
value?: string;
}
Rules
- Use
React.ChangeEvent<HTMLInputElement>for<input>changes. - Prefer
e.currentTarget.valuefor safer typing. - Do not redefine
targetin component props. - Use DOM element types like
HTMLInputElement, not your React component class. - For reusable input wrappers, prefer
React.InputHTMLAttributes<HTMLInputElement>.
Common element types
React.ChangeEvent<HTMLInputElement>
React.ChangeEvent<HTMLTextAreaElement>
React.ChangeEvent<HTMLSelectElement>
Good default pattern
type Props = React.InputHTMLAttributes<HTMLInputElement>;
Avoid
(e.target as any).value
React.HTMLProps<Input>
FAQ
How do I type onChange for a React input in TypeScript?
Use:
(e: React.ChangeEvent<HTMLInputElement>) => { ... }
Should I use target.value or currentTarget.value?
currentTarget.value is usually the safer and clearer choice in typed React handlers.
Why is React.HTMLProps<Input> wrong here?
Because Input is your React component class, not the DOM element type. For a real input element, use HTMLInputElement.
What is the correct props type for a reusable input component?
Usually:
React.InputHTMLAttributes<HTMLInputElement>
Can I still use an inline onChange handler?
Yes.
<input onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.currentTarget.value)} />
How do I type a textarea change event?
Use:
React.ChangeEvent<HTMLTextAreaElement>
Why should I avoid any here?
Because any turns off type checking and hides mistakes that TypeScript could catch for you.
Mini Project
Description
Build a small profile form with a reusable typed input component. This project demonstrates how to pass native input props through a custom React component and how to safely read value from a typed onChange event without using any.
Goal
Create a reusable Input component and use it in a form to edit a motto field with fully typed React change events.
Requirements
- Create a reusable
Inputcomponent that accepts standard HTML input props. - Type the
onChangehandler correctly for an HTML input element. - Store the input value in React state.
- Display the current motto below the input.
- Do not use
anyor manualtargetoverrides.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.