TypeScript with React Components ?
drLacheheb
Posted on September 11, 2024
In this post, I'm going to discuss why and how to use TypeScript to type React components.
You'll find how to annotate component props, mark a prop optional, and indicate the return type.
1. Why typing React components?
TypeScript is useful if you're coding middle and bigger size web applications. Annotating variables, objects, and functions creates contracts between different parts of your application.
For example, let's say I am the author of a component that displays a formatted date on the screen.
interface FormatDateProps {
date: Date
}
function FormatDate({ date }: FormatDateProps): JSX.Element {
return <div>{date.toLocaleString()}</div>;
}
According to the FormatDateProps
interface, the component FormatDate
accepts the date
prop as an instance of Date
. This constraint is crucial because the FormatDate
component uses the date.toLocaleString()
method, which only works with date instances.
Now, the user of the FormatDate
component must satisfy this constraint:
<FormatDate date={new Date()} />
If the user passes a string to date
instead:
<FormatDate date="Sep 28 2021" />
TypeScript will show a type error, ensuring you catch this issue during development.
2. Typing props
One of the best benefits React gains from TypeScript is prop typing.
Typing a React component is typically a 2-step process:
A) Define the interface that describes the component props.
B) Annotate the props parameter in the functional component.
For example:
interface MessageProps {
text: string;
important: boolean;
}
function Message({ text, important }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{text}
</div>
);
}
Now, when rendering Message
, props must match the defined types:
<Message text="The form has been submitted!" important={false} />
2.1 Props validation
If you provide incorrect props, TypeScript will show a type error during development:
<Message text="The form has been submitted!" important={0} />
2.2 children prop
The children
prop holds the content between the opening and closing tags of a component:
<Component>children</Component>
To type the children
prop:
interface MessageProps {
children: JSX.Element | JSX.Element[];
important: boolean;
}
function Message({ children, important }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
You can now pass children like so:
<Message important={false}>
<span>The form has been submitted!</span>
</Message>
or with multiple elements:
<Message important={false}>
<span>The form has been submitted!</span>
<span>Your request will be processed.</span>
</Message>
Challenge: How would you update the MessageProps
interface to also support a string value as a child?
2.3 Optional props
To make a prop optional, use ?
:
interface MessageProps {
children: JSX.Element | JSX.Element[];
important?: boolean;
}
function Message({ children, important = false }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
Now you can skip the important
prop:
<Message>
<span>The form has been submitted!</span>
</Message>
3. Return type
TypeScript can infer the return type of a React functional component, which is usually JSX.Element
:
function Message({ children, important = false }: MessageProps): JSX.Element {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
If the component may return null
, use JSX.Element | null
:
function ShowText({ show, text }: { show: boolean, text: string }): JSX.Element | null {
return show ? <div>{text}</div> : null;
}
3.1 Tip: enforce the return type
It's recommended to explicitly set the return type:
function BrokenComponent(): JSX.Element {
return
<div>Hello!</div>; // Error caught!
}
Feel free to comment below if you have any thoughts or questions!
Posted on September 11, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.