Question
What are the correct TypeScript types for React events in a form? I want to remove any from this component.
In particular, React.SyntheticEvent<EventTarget> does not allow access to e.target.name or e.target.value, because EventTarget does not guarantee that those properties exist.
export interface LoginProps {
login: {
[key: string]: string | Function
uname: string
passw: string
logIn: Function
}
}
@inject('login')
@observer
export class Login extends Component<LoginProps, {}> {
update = (e: React.SyntheticEvent<EventTarget>): void => {
this.props.login[e.target.name] = e.target.value
}
submit = (e: any): void => {
this.props.login.logIn()
e.preventDefault()
}
render() {
const { uname, passw } = this.props.login
return (
<div id="login">
<form>
<input
placeholder="Username"
type="text"
name="uname"
value={uname}
onChange={this.update}
/>
<input
placeholder="Password"
type="password"
name="passw"
value={passw}
onChange={this.update}
/>
<button type="submit" onClick={this.submit}>
Submit
</button>
</form>
</div>
)
}
}
Which event types should be used for the input change handler and form submission handler, and how can event properties be accessed safely?
Short Answer
React gives each JSX event a specific TypeScript event type. Use React.ChangeEvent<HTMLInputElement> when an input changes and React.FormEvent<HTMLFormElement> when a form is submitted. These types tell TypeScript which DOM element produced the event, so properties such as value and name are available safely.
Concept
React event handlers receive SyntheticEvent objects. A synthetic event is React's cross-browser wrapper around a native browser event.
The important part is that an event has an associated element type:
- An
<input>change handler works with anHTMLInputElement. - A
<form>submit handler works with anHTMLFormElement. - A
<button>click handler works with anHTMLButtonElement.
EventTarget is deliberately broad. It represents any possible event source, including objects that do not have name or value. Therefore, TypeScript correctly rejects this:
const target: EventTarget = event.target
target.value // Error: EventTarget does not guarantee a value property
For form controls, use the specific React event type. In an input change handler, React.ChangeEvent<HTMLInputElement> makes the input's value, name, checked, and other input properties available.
For submitting a form, prefer putting the handler on the <form onSubmit={...}> element rather than handling a button click. This supports clicking the button, pressing Enter in an input, and other normal browser form behavior.
Mental Model
Think of EventTarget as a package delivered from an unknown sender. You cannot assume the sender has a name or value.
HTMLInputElement is a package clearly labelled input. Since TypeScript knows it came from an input, it lets you read input-specific details such as value and name.
An event type such as React.ChangeEvent<HTMLInputElement> is that label: it says, “this is a change event from an input element.”
Syntax and Examples
Use the React event type that matches both the event and the element.
function LoginFields() {
const update = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(event.currentTarget.name)
console.log(event.currentTarget.value)
}
const submit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
console.log('Form submitted')
}
return (
<form onSubmit={submit}>
<input name="uname" onChange={update} />
<input name="passw" type="password" onChange={update} />
<button type="submit">Submit</button>
</form>
)
}
event.currentTarget is usually the clearest choice in a handler:
currentTargetis the element whose JSX prop owns the handler.targetis the element where the event started. It can be a nested element when events bubble.
For an onChange handler directly attached to an <input>, both commonly refer to the input, but currentTarget communicates the safer intention.
Common React event types include:
React.ChangeEvent<HTMLInputElement>
React.FormEvent<HTMLFormElement>
React.MouseEvent<HTMLButtonElement>
React.KeyboardEvent<HTMLInputElement>
React.FocusEvent<HTMLInputElement>
React.ClipboardEvent<HTMLInputElement>
Step by Step Execution
Consider this controlled input:
const update = (event: React.ChangeEvent<HTMLInputElement>) => {
const field = event.currentTarget.name
const value = event.currentTarget.value
console.log(field, value)
}
<input name="uname" value="ada" onChange={update} />
When a user changes ada to adam:
- The browser detects the input value change.
- React calls
updatewith a change event. - The event type says
currentTargetis anHTMLInputElement. event.currentTarget.nameevaluates to"uname".event.currentTarget.valueevaluates to"adam".- The handler can use those values to update the matching piece of state.
For form submission:
const submit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
// Send login data here.
}
- The user clicks a submit button or presses Enter.
- The form emits a submit event.
- React calls
submit. preventDefault()stops the browser's normal page navigation/reload.- Your application can validate and send the form data.
Real World Use Cases
Typed React events are useful wherever user interaction produces data.
- Login forms: Read a username or password with
ChangeEvent<HTMLInputElement>and send it in aFormEvent<HTMLFormElement>handler. - Checkbox settings: Use
ChangeEvent<HTMLInputElement>and readevent.currentTarget.checked. - Search boxes: Use a typed change handler to update a query as the user types.
- Keyboard shortcuts: Use
KeyboardEvent<HTMLInputElement>orKeyboardEvent<HTMLDivElement>to inspectevent.key. - File uploads: Use
ChangeEvent<HTMLInputElement>and readevent.currentTarget.files. - Buttons and menus: Use
MouseEvent<HTMLButtonElement>when a click handler needs button-specific information.
Typing events prevents mistakes such as reading .checked from a text input incorrectly or reading .value from a form element.
Real Codebase Usage
In production React code, developers usually combine typed handlers with controlled state, validation, and early returns.
A common form pattern is:
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (!username.trim() || !password) {
setError('Username and password are required.')
return
}
await login({ username, password })
}
For a small form, separate handlers are often the most type-safe and readable:
const onUsernameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setUsername(event.currentTarget.value)
}
const onPasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setPassword(event.currentTarget.value)
}
When multiple fields share one handler, developers constrain field names with a union type. This prevents accidentally updating an unknown key:
type LoginField = 'uname' | 'passw'
const update = (event: React.ChangeEvent<HTMLInputElement>) => {
const field = event.currentTarget.name as LoginField
const value = event.currentTarget.value
setLogin(previous => ({ ...previous, [field]: value }))
}
In many codebases, inline handlers can avoid explicit event annotations because TypeScript infers them from JSX:
<input onChange={event => setUsername(event.currentTarget.value)} />
Add an explicit type when the handler is declared separately, reused, exported, or when inference is not available.
Common Mistakes
Using EventTarget for input values
This fails because EventTarget might not be an input:
const update = (event: React.SyntheticEvent<EventTarget>) => {
console.log(event.target.value) // Error
}
Use a specific event type instead:
const update = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(event.currentTarget.value)
}
Using any for submit events
const submit = (event: any) => {
event.preventDefault()
}
This loses checking and autocomplete. Use:
const submit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
}
Handling form submission only with onClick
<button type="submit" onClick={submit}>Submit</button>
This can miss submission paths such as pressing Enter. Put the handler on the form:
<form onSubmit={submit}>
<button type="submit">Submit</button>
</form>
Using Function as a type
Function accepts nearly any callable value and does not describe its parameters or return value.
logIn: Function
Prefer a precise function type:
logIn: () => void
Allowing arbitrary string keys unnecessarily
An index signature such as [key: string]: string | Function allows unexpected properties and mixes incompatible kinds of values. Keep data and methods explicitly typed when possible.
Casting without checking
This silences TypeScript but can hide a real mismatch:
const value = (event.target as HTMLInputElement).value
Prefer correctly typing the handler. Use a cast only when the event truly may originate from multiple kinds of elements and you have verified the element at runtime.
Comparisons
| Situation | Recommended type | Useful properties |
|---|---|---|
| Text, password, email, or number input changes | React.ChangeEvent<HTMLInputElement> | currentTarget.value, name, checked, files |
| Textarea changes | React.ChangeEvent<HTMLTextAreaElement> | currentTarget.value, name |
| Select changes | React.ChangeEvent<HTMLSelectElement> | currentTarget.value, selectedOptions |
| Form submission | React.FormEvent<HTMLFormElement> | preventDefault(), currentTarget |
| Button click | React.MouseEvent<HTMLButtonElement> | button, mouse coordinates, currentTarget |
| Keyboard input | React.KeyboardEvent<HTMLInputElement> | key, code, modifier keys |
| Choice | When to use it |
|---|---|
event.target | You need the element where the event originally began. It may be a nested descendant. |
event.currentTarget | You need the element that owns the handler. This is usually preferable for typed React handlers. |
React.SyntheticEvent | You only need generic event operations and do not need element-specific properties. |
| Specific React event type | You need properties belonging to a particular element or event kind. |
Cheat Sheet
// Input value
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.currentTarget
}
// Checkbox state
const onCheck = (event: React.ChangeEvent<HTMLInputElement>) => {
const checked = event.currentTarget.checked
}
// Textarea
const onText = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = event.currentTarget.value
}
// Select
const onSelect = (event: React.ChangeEvent<HTMLSelectElement>) => {
const value = event.currentTarget.value
}
// Form submit
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
}
// Button click
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log(event.currentTarget.disabled)
}
// Keyboard event
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
// ...
}
}
- Prefer
event.currentTargetfor the element that owns the handler. - Do not use
EventTargetwhen you need.value,.name, or.checked. - Put submission logic on
form onSubmit, not only on a submit button'sonClick. - Prefer
() => voidoverFunctionfor known callbacks.
FAQ
What is the TypeScript type for React input onChange?
Use React.ChangeEvent<HTMLInputElement> for an <input> element.
Why does event.target.value cause an error in TypeScript?
The event may be typed with EventTarget, which is too general to guarantee a value property. Type the event as React.ChangeEvent<HTMLInputElement> and use event.currentTarget.value.
What is the React TypeScript type for form submission?
Use React.FormEvent<HTMLFormElement>. Call event.preventDefault() when handling the submission in JavaScript.
Should I use onClick or onSubmit for a form button?
Use onSubmit on the <form>. It handles both submit-button clicks and keyboard submission such as Enter.
Can TypeScript infer React event types automatically?
Yes, for inline JSX handlers. A separately declared handler usually needs an explicit annotation.
What type should I use for checkbox changes?
Use React.ChangeEvent<HTMLInputElement> and read event.currentTarget.checked rather than value.
Is React.SyntheticEvent ever useful?
Yes, when you only need generic event methods such as preventDefault() and do not need properties specific to an input, form, or button.
Why is currentTarget often safer than target?
currentTarget is the element that has the handler. target can be a nested child where the event began, especially for bubbling events.
Mini Project
Description
Build a typed login form with username and password fields. The project demonstrates how one onChange handler can safely update multiple fields and how a form-level onSubmit handler prevents the browser's default submission behavior.
Goal
Create a login form that displays the submitted username without using any.
Requirements
Use React and TypeScript state for uname and passw.
Use React.ChangeEvent<HTMLInputElement> for the shared input handler.
Use React.FormEvent<HTMLFormElement> for form submission.
Prevent the default browser form submission.
Show a validation message when either field is empty.
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.