Typescript in React
Jareth Tan
Posted on August 10, 2022
Table Of Contents
1. Introduction
2. Functionalities and Examples
3. Conclusion
Another week, another post. Carrying on from my previous post about Typescript for beginners, I thought I would write a post about using typescript in a React application for beginners.
Introduction
As the popularity of React exploded in recent years, the demand to build React applications using Typescript has increase significantly as well.
Adding Typescript to React
I typically use create-react-app boilerplate to start my react application. To add Typescript when creating our react app, just type this additional wordings:
npx create-react-app my-app --template typescript
Once the boilerplate is completed, open the file and we will notice that some files are named .ts
or .tsx
. There is also a new file generated called tsconfig.ts
. This file is where all the magic happens for Typescript. Typescript looks for the tsconfig.json file in the project root folder, and that file provides configuration options for the compiler.
Before we jump into the use cases of Typescript in React, lets have a quick overview on the different "Types" in Typescript.
- void: denotes the absence of any type.
- tuple: works like an array, but the number of elements here is fixed. The types of elements in a tuple are known and can be of different types.
- enum: is a group of constant values that are closely related and known.
- any: allows us to assign the value of any type to a variable. Used when a value type is unknown
- never: is a type that contains no value, so we can’t assign any value to a variable with a never type.
- union: this describes a value that can be one of several types number | string | boolean
- object: To define an object type we list its properties and their types: {x:number, y:number}
There is a deeper dive on this types in the previous post. Do check it out for more information on this.
React functional components
With the release of react 16.8, majority of users have shifted away from using class components to functional components. A React Functional Component is a function that receives props objects and returns JSX elements. In React components, we need to consider the type of props coming in. Integrating Typescript with functional component is pretty straight forward as follows:
import {FC} from "react"
const Movies: FC<{title: string}> = ({ title }) => {
return (
<>
<h1>{message}</h1>
</>
);
};
This is one of the ways to define a functional component with Typescript. We have assigned a FC
type to the functional component. Well, FC is an alias for Function Component. Also, noticed we have assigned types for the props being passed into the component by declaring it right after FC
.
Interface
Another way of assigning types to your props is by using an interface. Typescript interfaces are essential to enforcing a particular object shape. An interface is like an object that contains information about the object’s properties and types. It explicitly tells the Typescript compiler about the property names and the value types an object can have. Also, since TypeScript is able to infer the type of your variables, you can remove typing the component FC as well. So in the end, it will look like this:
import {FC} from "react"
// we can do it like this
const Movies: FC<{title: string, boxOffice: number}> = ({ title, boxOffice }) => {
return (
<>
<h1>{message}</h1>
</>
);
};
// or with an interface
interface MovieProps{
title: string;
boxOffice: number;
}
const Movies = ({ title, boxOffice }: MovieProps) => {
return (
<>
<h1>{message}</h1>
</>
);
};
// To keep the title prop optional. Just pass it this way
interface MovieProps{
title?: string;
boxOffice: number;
}
There are a few advantages for using interfaces. Firstly, the code is a little cleaner. Secondly, we can export the interface and use it in other parts of our code as a type as well by doing this: export interface MovieProps
which ensures consistency in our code. We can also define our interfaces in a single file and reference from that file. Any changes made can also be made in one location.
Some React Hooks
useState
For useState
hooks, the type expected can be inferred from the initial value passed into useState
. For example:
const [title, setTitle] = useState("");
// is equal to
const [title, setTitle] = useState<string>("");
Since an empty string is passed as an initial value, typescript has inferred that the value stored will be a string type. However, if we are going set initial value as null
or undefined
, we need to pass a union
of types like this:
const [title, setTitle] = useState<string | null>(null);
And if we are expecting an object in the useState
hook, we can use interface
to define the object type and assign it as a type in useState.
useRef
In most cases, useRef is used to reference input elements in HTML. Something like this:
function movieSearch() {
const inputTitle = useRef(null);
return (
<>
<input ref={inputTitle} type="text" />
</>
);
}
In such cases, we can use a generic type and note that we dont need to assign null as a type for generic types as it accepts null
already.
const inputTitle = useRef<HTMLInputElement>(null)
useEffect
Typing the useEffect hook is not required because they don’t deal with returning values. The cleanup function for the useEffect hook is not considered a value that can be changed either so we can write these hooks as normal.
HTML events
The most commonly HTML events are button events, onChange events and form submits. These are the following example on how to type those events.
import { useState, ReactElement, ChangeEvent, FormEvent } from "react";
const App = (): ReactElement => {
const [title, setTitle] = useState<string | null>(null);
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
// handle event here...
};
return (
<form onSubmit={handleSubmit}>
<div>
<span>Email:</span>
<input
type="email"
name="email"
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setTitle(e.currentTarget.value)
}
/>
</div>
<div>
<input type="Submit" value="Submit" />
</div>
</form>
);
};
As seen from above, the events for submitting the form is typed as FormEvent
as import from the react node module. As for the change event, it is typed as ChangeEvent<HTMLInputElement>
as onChange prop is in an input element which handles a change event. as for button events, it is shown as:
<button onClick={ (e: MouseEvent<HTMLButtonElement, MouseEvent>) => console.log("Clicked")}>button</button>
Do note that for most of the time, button and change event types can be inferred by Typescript hence there is no need to explicitly assigned the types to such events.
Conclusion
Well, there we go, this are the more commonly used types when using React with Typescript. This list is by no means exhaustive and I do realize I have not covered some critical parts such as generics, type definition for some hooks (useContext or useMemo) or typing different type of React components. However I have not apply Typescript consistently on these topics or understand the core concepts behind them to write about it confidently yet. Once I do, I will update this blog post with more information. For now, I believe the information in this post is sufficient enough to get start with Typescript in a React application. As always, more information can be found in the official document: https://www.typescriptlang.org/docs/handbook/react.html
Till next time. Take care and stay safe.
Posted on August 10, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.