How To Strongly Type process.env
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
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:
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
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
Discover when it's appropriate to use TypeScript's any
type despite its risks. Learn about legitimate cases where any
is necessary.
Learn why TypeScript's types don't exist at runtime. Discover how TypeScript compiles down to JavaScript and how it differs from other strongly-typed languages.
Improve React TypeScript performance by replacing type & with interface extends. Boost IDE and tsc speed significantly.
In this book teaser, we discuss deriving vs decoupling your types: when building relationships between your types or segregating them makes sense.
Learn how TypeScript's new utility type, NoInfer, can improve inference behavior by controlling where types are inferred in generic functions.