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:
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 where is your React class.
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.();
= () => {
nextValue = e..;
(nextValue);
};
;
}
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<>) => {
value = e..;
(value);
(value. < );
};
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.
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 props
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
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.
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..)} />
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.
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.
Angular Dependency Injection: Fix "Can't Resolve All Parameters for Component" Errors
Learn why Angular shows "Can't resolve all parameters for component" and how to fix service injection issues in components.