Forms and Events

If performance is not an issue, inlining handlers is easiest as you can just use type inference and contextual typing:

const el = (
<button
onClick={(event) => {
/* ... */
}}
/>
);

But if you need to define your event handler separately, IDE tooling really comes in handy here, as the @type definitions come with a wealth of typing. Type what you are looking for and usually the autocomplete will help you out. Here is what it looks like for an onChange for a form event:

class App extends React.Component<
{},
{
// no props
text: string;
}
> {
state = {
text: "",
};
// typing on RIGHT hand side of =
onChange = (e: React.FormEvent<HTMLInputElement>): void => {
this.setState({ text: e.currentTarget.value });
};
render() {
return (
<div>
<input type="text" value={this.state.text} onChange={this.onChange} />
</div>
);
}
}

View in the TypeScript Playground

Instead of typing the arguments and return values with React.FormEvent<> and void, you may alternatively apply types to the event handler itself (contributed by @TomasHubelbauer):

// typing on LEFT hand side of =
onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
this.setState({text: e.currentTarget.value})
}
Why two ways to do the same thing?

The first method uses an inferred method signature (e: React.FormEvent<HTMLInputElement>): void and the second method enforces a type of the delegate provided by @types/react. So React.ChangeEventHandler<> is simply a "blessed" typing by @types/react, whereas you can think of the inferred method as more... artisanally hand-rolled. Either way it's a good pattern to know. See our Github PR for more.

Typing onSubmit, with Uncontrolled components in a Form

If you don't quite care about the type of the event, you can just use React.SyntheticEvent. If your target form has custom named inputs that you'd like to access, you can use type widening:

<form
ref={formRef}
onSubmit={(e: React.SyntheticEvent) => {
e.preventDefault();
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
const email = target.email.value; // typechecks!
const password = target.password.value; // typechecks!
// etc...
}}
>
<div>
<label>
Email:
<input type="email" name="email" />
</label>
</div>
<div>
<label>
Password:
<input type="password" name="password" />
</label>
</div>
<div>
<input type="submit" value="Log in" />
</div>
</form>

View in the TypeScript Playground

Of course, if you're making any sort of significant form, you should use Formik, which is written in TypeScript.