Using TypeScript With React

Cover Image

JavaScript is inherently a weakly typed programming language, meaning that when you declare a variable, you don’t have to specify its type. You can just use the var, let, or const keywords. These variables could contain numbers, strings, objects, etc. This may seem convenient, but it makes the codebase much more error prone. Time that could be spent writing new features is spent fixing bugs instead.

This is the way JavaScript had always been up until 2012 when Anders Hejlsberg, architect behind C#, created TypeScript (talk about an innovative guy).

TypeScript is a superset of JavaScript that adds typing to the language. This means that issues can be caught at compile time. Compare this to saving your file, running your app, and then seeing it crash just because you misspelled a property name (happens to the best of us). You might think that it’s not a big deal, but scenarios like this add up over time. This is especially true when the codebase is large and many developers are working on different parts of the app. Being able to ensure a variable type prevents you from spending time searching the codebase and makes the code more readable.

TypeScript is compatible with all the major frontend frameworks. However, in this post, I am going to cover how we can use it with React specifically. I will show how we can create a new React project with TypeScript and also how we can incorporate TypeScript into an existing app.

And as great as TypeScript is, it’s also important to note that not everyone supports it. Some argue that it’s too bloated and doesn’t add enough value to warrant using it. So I will also go into that and talk about if it makes sense to always use TypeScript.

With that said, this is a topic I have been excited to cover so let’s get into it.

Creating a New Project With TypeScript

To demonstrate TypeScript in React, we’re going to create an “idea list app”. It will be where we jot down all of our great ideas so we don’t forget them.

If at anytime you would like to check out the final source code, you can find it here.

Initial Setup

To use TypeScript in a new React app, you can check this doc as a reference. But basically, you just want to open up a terminal and run create-react-app with an adjustment:

Create a React App With TypeScript

The first thing you may notice that’s different from a traditional React project is that all of the .js files have been changed to .tsx. A .tsx file is a TypeScript file written with JSX syntax. Additionally, there are a few .ts files (TypeScript) and we have a configuration file, tsconfig.json:

TypeScript Files

First, let’s simplify this code by removing App.css, App.test.tsx, logo.svg, reportWebVitals.ts, and setupTests.ts:

Delete Unnecessary TypeScript Files

TypeScript Files Cleaned Up

We’ll have to then change App.tsx and index.tsx to resolve the errors:

If you’re looking at these .tsx files and thinking that they just look like JSX, you’re right. Let’s change our App.tsx file to use typescript:

Now, we’re saying that our App component must be of type React.FC, a.k.a. React Functional Component. Our App component wasn’t annotated with React.FC upon creation because it is in unnecessary as of React version 18. The reason for this is the children prop was removed in version 18. And not only is React.FC unnecessary, it is discouraged. I am including it here so you are aware, and you may see it when working with legacy code. If you would like a more extensive explanation on why React.FC should be mostly avoided, you can read about it here.

Next, let’s add a bit of styling to index.css:

Finally, let’s bring in React Bootstrap so our app won’t look too plain. In the terminal, run:

Install React Bootstrap

And we need to bring that into our index.tsx so we can use it:

It’s important that our reference to Bootstrap comes before our reference to index.css. Otherwise, our styling will not be applied.

Creating the Idea List

Inside of our src folder, let’s create a components folder; and inside of that folder, let’s create create a file called IdeaList.tsx where we define an ideas array and iterate over it:

Let’s incorporate some Bootstrap into our App component and also bring in our IdeaList component:

Run the app, and you should see our first idea in the browser:

Idea List App

This is working and it’s great, but eventually we will want to have our idea list reside in the App component. Because we will want to be able to create new ideas with a separate component and we will likely manage them via our App component. Let’s go ahead and move it there and pass it as a prop to IdeaList.tsx:

However, once you add this code, you’ll notice an error:

TypeScript Error

We can finally see TypeScript in action. What it’s saying here is that it doesn’t know what type the incoming props are for our IdeaList. TypeScript requires that we explicitly define any props that we pass as arguments to our components. So we need to define an interface for our type. We could define this inside of IdeaList, but in a real world application, it would probably be better to use a separate file to keep the code clean. So in our src folder, create a folder interfaces, and in that folder create a new file ideaList.interface.ts:

Here we are defining two interfaces, IIdea and IIdeaList (I know the double I looks weird, but it would be best practice to name it like this). We could have just wrote one interface that looked like this:

But personally I would rather create a separate type. Inside of our IIdea interface, we give our id and our text properties the types of number and string respectively.

Next, we need to bring our interface into our IdeaList and we also need to accept ideas as an argument and give it a type of IIdeaList:

With that, our app should be working just as before, except now it is backed with the typing of TypeScript.

Adding Ideas

It’s time to add the functionality for adding new ideas to our app. Inside of the components folder, let’s create a new file IdeaForm.tsx:

And for this, we need a new interface to define our props. In the interfaces folder, create a new file ideaFormProps.interface.ts:

In our IdeaForm, we can see a few differences between it and a typical JSX file. Just like in our IdeaList component, we created an interface type for our props, but in addition to this, TypeScript expects us to type the parameters of our event handlers. This isn’t extremely useful for developers as most people looking at this would know that it is an Event type. However, as we will see later, is very useful for functions where it isn’t as obvious what type the parameter is supposed to be.

Now, we need to bring the IdeaForm into our App component and also make some other changes. We need to use the useState hook in place of our hard coded array. Additionally, we need to create an event handler for adding items to our idea list so that we can pass it to IdeaForm and call it there:

Now we can see our app has a form:

Adding a Form to the App

With those changes, we should now be able to successfully add ideas to our list:

Adding Items to Our Idea List

And that is how you use TypeScript in a new React application. Next, we’ll implement TypeScript into an existing application.

Adding TypeScript to an Existing Project

To demonstrate adding TypeScript to an existing React project, I am actually going to use the side project I am working on at the moment. I am recreating the ladder from my favorite video game, Warcraft III, the best game of all time in my opinion.

Initial Setup

The up-to-date source code can be found here. To clone the repo, open up a terminal and run:

Clone Warcraft 3 Ladder App
I already integrated TypeScript into the project, so to revert back to the commit before I made TypeScript changes, you’ll need to checkout this commit by running the following command in the terminal:

Git Checkout Before TypeScript

Next, to finish setting up our project, we need to install TypeScript. However, this project is structured differently in that our React application resides in the frontend folder. So, we need to navigate to the frontend folder from the root folder and then install TypeScript there. Run the following commands in the terminal (this assumes you start in the root folder):

Install TypeScript in an Existing App

Now, we need to create a tsconfig.json file in the frontend folder:

A whole post could be made about the options here and how they work. I will leave that research to you; I may make a post on that in the future. In the meantime, you can check out this doc. The important thing to know is that it is required in all TypeScript projects because the TypeScript compiler api requires it. The compiler uses it to find the root folder of the project and tsconfig.json tells the compiler how to compile the project.

Lastly, to run the app, from the frontend folder, run:

Run the App

Integrating TypeScript

The first and easiest changes we can make are to change App.js and components/Header.jsx to App.tsx and components/Header.tsx respectively. If we had code here that required typing, we would get errors and have to make additional changes. Fortunately, we don’t.

The app is using Redux Toolkit for state management. I will be going through these changes fairly quickly, my aim is to give an overview of what changes between a regular React app and one that implements TypeScript. I followed this doc to make these changes, and you can check it out for more in-depth explanations.

Instead of the useSelector() and useDispatch() hooks that we used in my last post on Redux, we will use the RootState and AppDispatch types to create typed hooks. In the app folder, change store.js to store.ts and add two lines at the bottom:

Next, in the app folder, create a new file hooks.ts where we will define useAppSelector and useAppDispatch hooks to replace useSelector and useDispatch respectively:

Now, we can start implementing the typed hooks we defined into our application. In the components folder, let’s change change Pagination.jsx to Pagination.tsx. We will get some type errors. Let’s fix those.

To start, replace the line where we import useSelector and useDispatch with the typed hooks we just defined:

And then, we need to replace all of the places where we were calling useSelector and useDispatch:

To wrap up our changes in the Pagination component, we need to go to our paginate() function and give the number parameter a type of number (I should have probably named this parameter pageNumber):

Similarly to how we did the Pagination component, in the pages folder, we can change Leaderboard.jsx to Leaderboard.tsx. And we will have to make some changes to fix the errors.

Our compareXp() function accepts two “player” objects. We will need to create an interface like we did in the first section. In the src folder, create an interfaces folder. And in there, create a player.interface.ts file:

We need to bring that into Leaderboard.jsx:

And specify both of our parameters in compareXp() to be our new IPlayer type:

We need to set types for the rest of our functions. I won’t go through them one-by-one, but we need to replace what we have with this:

We already went through replacing useSelector and useDispatch so I skipped that too. One thing I want to mention about the above code that we haven’t covered is that in our getClanId() function, TypeScript forces us to use optional chaining with ?.. This is to ensure code safety, if clans.find() returns null or undefined, our method will return undefined instead of throwing an error.

With those changes, we are pretty much done with integrating TypeScript into this project!

Final Thoughts

When I first converted the above project to TypeScript, it was purely written in JavaScript/JSX. I was worried about how much work it would be, but it turned out to be a lot easier than I expected. Making the changes for Redux was the most challenging part, but that wasn’t too bad either.

Personally, I can see how TypeScript could be beneficial in a large code base, especially with function parameters. When other developers come to work on functions that you wrote, it may be unclear what type the parameters should be. For example, in my project my playerId parameter was a string instead of a number. An experienced developer would probably figure that out quickly, but regardless, time and energy would be saved having explicit types. At the same time, I can see how people could argue that TypeScript is too bloated for the benefit it provides. I will personally be using TypeScript in future projects as I enjoyed using it.

If you enjoyed this post, make sure to subscribe so you don’t miss future MERN stack content!

To learn how to use TypeScript with React, I used Maximilian Schwarzmüller’s Understanding TypeScript course on Udemy. Check out his course along with his other content if you want to level up your web development skills.