TypeScript Tip 02: Three Different Ways to Annotate Event Handlers

Muggle-born
2 min readApr 30, 2022

When it comes to annotating event handlers, you have several different options. Below we’ll cover three different approaches you can use.

Use inline handlers and let TypeScript do the work for you

In TypeScript, there are several places where type inference is used to provide information when there is no explicit type annotation. The most basic example of this would be:

let counterOne = 0;// ❌ no need to add a type annotation
let counterTwo: number = 0;

In the example above, TypeScript infers that counterOne is a number type. We didn’t have to provide the type annotation ourselves!

The same idea applies to event handlers in React:

function Component() {
return (
<button
onClick={(event) => {
/* event will be correctly typed automatically! */
}}
/>
)
}

Typing the right-hand side of =

Often, you’ll need to define the type of your event handler separately. For example, maybe you have written an onClick function to handle what happens when a user clicks a button. One approach is to provide types for the parameters and return values as shown below:

function Button() {
const handleOnClick = (event: React.MouseEvent<HTMLButtonElement>): void => {
/* do something */
}
return <button onClick={handleOnClick}>click me</button>
}

Typing the left-hand side of =

Alternatively, instead of specifying the types of the function parameters and return values, you can leverage React’s EventHandler types. These types provide the same type-safety as typing the right-hand side of the =

function Button() {
const handleOnClick: React.MouseEventHandler<HTMLButtonElement> = (event) => {
/* do something */
}

return <button onClick={handleOnClick}>click me</button>
}

👋 That’s all for this week; try solving the problems below if you fancy a challenge. Otherwise, come back next week for another tip!

Challenge for the Reader

  1. What is the difference between the two event handlers?
function handleOnChange(event: React.FormEvent<HTMLInputElement>): void {
// …
}
const handleOnChange: ChangeEventHandler<HTMLInputElement> = (event) => {
// ..
}

a) They have two different return types.
b) Nothing is different. They are effectively equivalent.
c) The event parameter in one has different types than in the other.
d) Both a and c are correct

a) They have two different return types.
b) Nothing is different. They are effectively equivalent.
c) The event parameter in one has different types than in the other.
d) Both (a) and © are correct

Remove all the TypeScript errors in my custom TS Playground to test your knowledge of Typing Event Handlers

Additional Resources

--

--

Muggle-born

Hi, my name is Jeremiah. I build things on the web for fun. I enjoy figuring how and why things work the way they do, and I try teaching others along the way.