The TypeScript 5.3 Feature They Didn't Tell You About
TypeScript 5.3 introduces relaxed rules around readonly arrays and improvements in const type parameters.
When you're working with React and TypeScript, you'll often encounter this kind of error:
tsx
constParameter 'e' implicitly has an 'any' type.7006Parameter 'e' implicitly has an 'any' type.onChange = () => {}; e <input onChange ={onChange } />;
It's not always clear what type you should give to the e
inside your onChange
function.
This can happen with onClick
, onSubmit
, or any of the other event handlers that DOM elements receive.
Luckily, there are several solutions:
The first solution is to hover over the type of the thing you're trying to pass in:
tsx
<input onChange ={onChange } />;
As you can see, this outputs an astonishingly long type:
txt
React.InputHTMLAttributes<HTMLInputElement>.onChange?:React.ChangeEventHandler<HTMLInputElement> | undefined
The part we want is this: React.ChangeEventHandler<HTMLInputElement>
.
We can use that to type our onChange
function:
tsx
importReact from "react";constonChange :React .ChangeEventHandler <HTMLInputElement > = (e ) => {console .log (e );};<input onChange ={onChange } />;
Sometimes, you don't want to type the entire function. You just want to type the event.
To extract the right event type, you need to do a slightly different dance.
First, create an inline function inside onChange
.
tsx
<input onChange ={(e ) => {}} />
Now you have access to e
, you can hover over it and get the correct event type:
tsx
<input onChange ={(e ) => {}} />
Finally, you can copy that type and use it to type your onChange
function:
tsx
importReact from "react";constonChange = (e :React .ChangeEvent <HTMLInputElement >) => {console .log (e );};<input onChange ={onChange } />;
This still feels slow, though. Is there a better way?
React.ComponentProps
A way to speed this up would be to remove the step where we check the type of the handler. It would be great to say 'I want this sort of handler type' and have TypeScript figure out the rest.
For this, we can use a type helper called ComponentProps
, which I've written about before.
tsx
importReact from "react";constonChange :React .ComponentProps <"input">["onChange"] =(e ) => {console .log (e );};<input onChange ={onChange } />;
By passing input
to ComponentProps
, we're telling TypeScript that we want the props for the input
element.
Then, we grab the onChange
property from those props, and use it to type our function.
Thanks to Sebastien Lorber for this tip!
EventFrom
HelperThis is really nice, but we're still back to having to type the onChange
function.
What if we want to extract just the event type?
We could use a combination of Parameters
, NonNullable
, and indexed access types to get there:
tsx
importReact from "react";constonChange = (e :Parameters <NonNullable <React .ComponentProps <"input">["onChange"]>>[0]) => {};
But that's far too much code to write.
Instead, let's imagine a type helper called EventFor
:
tsx
constonChange = (e :EventFor <"input", "onChange">) => {console .log (e );};<input onChange ={onChange } />;
This takes in the element type and the handler type, and returns the event type. You get autocomplete on each of the parameters, and you don't have to type the function.
The issue is, you need to keep a relatively large type helper in your codebase. Here's the code:
ts
typeGetEventHandlers <T extends keyofJSX .IntrinsicElements > =Extract <keyofJSX .IntrinsicElements [T ], `on${string}`>;/*** Provides the event type for a given element and handler.** @example** type MyEvent = EventFor<"input", "onChange">;*/export typeEventFor <TElement extends keyofJSX .IntrinsicElements ,THandler extendsGetEventHandlers <TElement >> =JSX .IntrinsicElements [TElement ][THandler ] extends| ((e : inferTEvent ) => any)| undefined?TEvent : never;
Personally, I prefer the EventFor
solution. That might be because I came up with it, but here's why I like it:
But, if you don't want to keep a type helper around, the ComponentProps
solution is a great alternative.
Share this article with your friends
TypeScript 5.3 introduces relaxed rules around readonly arrays and improvements in const type parameters.
Learn how to provide a TypeScript playground when asking for help with your TypeScript questions, making it easier for others to assist you.
A step-by-step guide on setting up ESBuild to bundle a Node application.
When using '--moduleResolution' with the option 'nodenext', it is necessary to add explicit file extensions to relative import paths in EcmaScript imports.
Learn how to add TypeScript to your existing React project in a few simple steps.
Learn the essential TypeScript configuration options and create a concise tsconfig.json file for your projects with this helpful cheatsheet.