All posts

How to Migrate your React Project to TypeScript

How to Migrate your React Project to TypeScript

As developers, one of the most important parts of the process is to ensure the reliability of the software that you’re creating. If you’re working with a team, it’s important to maintain readability, consistency, and dependability. One way to do this is to use TypeScript. Now if you’re like me, you might already have a project written with a frontend framework that doesn’t use TypeScript. Never fear, TypeScript allows you to incrementally migrate your project. Today, we’re going to begin migrating my React project, Affirmation, to TypeScript.

5 Reasons to Migrate to TypeScript

There are a lot of reasons–including the ones above–to migrate to TypeScript, but in case those didn’t convince you, here are five more:

  • Decreases bugs in your code

  • Improves onboarding of new contributors

  • Decreases the need for tests

  • Often makes projects easier to maintain

  • Allows you to remove technical debt more quickly

Getting Started

Since we’re building on an existing project, here are some resources to familiarize yourself with:

The first thing we want to do is get the app running as is so we can ensure that as we upgrade, we won’t be making any breaking changes.

Run the React Project

  • Clone the repository.

  • Create a Deepgram API Key with an admin or owner role - get it here.

  • Create a file called .env and add DG_KEY='your-API-key' Run npm i in your terminal.

  • Run npm run start in your terminal.

  • Run node server/server.js in your terminal.

If it doesn’t automatically open, navigate to http://localhost:3000/. You should see the project running.

Adding TypeScript

In your terminal run npm install --save typescript @types/node @types/react @types/react-dom @types/websocket.

At the project root level, create a new file called tsconfig.json. The tsconfig.json file allows you to add specifications and compiler options for the project. You can either create the file yourself or run npx tsc --init in your terminal. With the second option, you’ll see that there are some active default options and a lot of options commented out that you can uncomment as needed.

Within our tsconfig.json you’ll see that we’re using "strict": true. When using this within the compilerOptions, TypeScript will validate as much as it can. This means there will be more checking, and more to update as you migrate your React project to TypeScript. But this also means you’ll get the most benefit from using TypeScript.

Migrate React to TypeScript

Now that everything is up and running, we can start our migration. Because the main file we’re working with is Affirmation.js, we’re going to start there. Rename this file to Affirmation.tsx and then open the file.

We should immediately see three lines that need to be updated, lines 9 and 12 should have an indication that the e needs to be typed, and line 58 should also indicate that socketRef.current needs to be addressed.

If you’re working in VSCode, you can hover over each of these areas to get more details about the problem. Let’s take them one at a time.

If we hover over the e on line 9, we see this message:

Now, we want to avoid having an any type. That doesn’t give us the protection that we want from TypeScript. So let’s take a look at the code to help us understand what this event is doing.

const handleChange = (e) => {
		setAffirmation(e.target.value)
	}

This is in our handleChange function that’s attached to our <textarea> element. When our user types in the <textarea>, the event is passed to the handleChange function and becomes the value for our setAffirmation hook. We know that we don’t want the type of our event to be any. This is a journal app and that value is text, or more specifically, a string.

We need to let the application know that we don’t want it to accept any other type. We’re going to give our function a type of React.ChangeEventHandler<HTMLTextAreaElement> since we have access to React.ChangeEventHandler through @types/react. This is what it will look like if you use inline types:

const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> 
	= (e) => {
		setAffirmation(e.target.value)
	}

The next update we need to make is in our handleSubmit function:

 const handleSubmit = (e) => {
		e.preventDefault()
		setFinalAffirmation(true)
	}

If we hover over the e, we get the same message we did above. You might be tempted to type it in the same way, but let’s think about what’s happening here for a second. We’re submitting a completed form here. If we head over to the React TypeScript Cheatsheet, we’ll find an event type of FormEvent, which they describe as an “Event that occurs whenever a form or form element gets/loses focus, a form element value is changed or the form is submitted.” That sounds like a good choice.

	/* submit is a form event */
    const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
		/* prevent page from refreshing */
		e.preventDefault()
		setFinalAffirmation(true)
	}

We add a comment there for clarity that submit is a form event, and then we specify the type as an HTMLFormElement.

We’ll move on to the last one in this example.

Around line 57, we can see that TypeScript is complaining about socketRef.current. If we hover over it, we get this message:

Because we added the WebSocket types package, @types/websocket, we have access to a WebSocket type. However, we don’t set the type on line 57. The actual problem is with const socketRef = useRef(null).

We need to let TypeScript know that we’re looking at a WebSocket or null: const socketRef = useRef<WebSocket | null>(null).

With that last update, we no longer have TypeScript complaining in this file. That doesn’t mean that there aren’t other updates that we can make. If you hover over other variables or functions, we can see what TypeScript is inferring.

To complete this migration, we need to update the .js files to .tsx. But the nice thing is that we don’t have to do that all at once. For now, this is where we’ll leave it.

Learn More About TypeScript

There’s a lot to learn about TypeScript. Here are some resources I've found helpful:

To access the code for this blog post, select the feature/typescript branch from the React App GitHub Repository. If you have any thoughts, comments, or questions, please drop a comment in our General Discussion category, or if you want to say hey, drop your intro in our intro thread.

If you have any feedback about this post, or anything else around Deepgram, we'd love to hear from you. Please let us know in our GitHub discussions.

More with these tags:

Share your feedback

Thank you! Can you tell us what you liked about it? (Optional)

Thank you. What could we have done better? (Optional)

We may also want to contact you with updates or questions related to your feedback and our product. If don't mind, you can optionally leave your email address along with your comments.

Thank you!

We appreciate your response.