- Getting Started: Convert a React Project to TypeScript
- Get the Compiler Running
- Disable noImplicitAny
- Enable Strict Mode
- Sign up for more like this.
- Rust Wasm on Fastly [email protected]
- Fast and Efficient AWS Lambdas Built With Rust
- A Real World Comparison between Cloudflare Workers and Fastly [email protected]
- How to Migrate a React App to TypeScript
- The Plan
- 1. Add TypeScript to the Project
- 2. Add the tsconfig.json
- 3. Start with a Simple Component
- 4. Convert All Files
- 5. Increase tsconfig.json Strictness
- 6. Clean Up Shortcuts
- 7. Celebrate
- Things to Remember
- Start Small
- Increase Strictness Over Time
- Lean on Shortcuts
- Further Reading
- Share This Article
Getting Started: Convert a React Project to TypeScript
As a superset of JavaScript, Typescript can work in conjunction with JavaScript, importing Typescript code into a JavaScript file and vice versa. This means that migrating to TypeScript can be done incrementally, far different from converting a codebase from one programming language to something unrelated.
However, it can be daunting to convert a large codebase to TypeScript without creating days or sometimes weeks of work.
The best way to embark on this transition is to make small, incremental changes to your codebase and quickly get those changes into main . You don’t want to have some long-running TypeScript branch while other members of your team are making changes upstream; that is setting yourself up for more work and a merge conflicts nightmare. Making focused efforts to push forward basic conversions to TypeScript will save a lot of time. Get your codebase converted to TypeScript before you type everything perfectly right away.
Get the Compiler Running
In this first step, set up the Typescript compiler with the most permissive settings. This is not the time to enable strict mode. In this first phase, disable noImplicitAny and rename all your files from .js to .ts (use this bash script).
< "compilerOptions":< "baseUrl": "src", "target":"es5", "allowJs": true, "skipLibCheck": true, "noImplicitAny": false, "moduleResolution": "node", "module": "esnext", "jsx": "preserve", "strict": false, >, "include":[ "src/**/*" ], "exclude":[ "node_modules" ], >
At this stage, only fix errors causing Typescript compiler errors, being careful to avoid functionality changes to the codebase.
Depending on your application, many of the errors you will find at this point involve defining what function parameters are required or optional, typing event onChange handlers, typing React Component props, etc.
Property 'children' is missing in type '< title: string; items: any[]; secondary: any; small: any; >' but required in type 'Pick, "items" | . 6 more . | "small"> & Pick & Pick, "items" | . 3 more . | "tertiary">'.ts(2741)
Don’t shy away from using the explicit any type at this point. You can add more meaningful types later.
Disable noImplicitAny
Next, set noImplicitAny to true. Your goal for this step will be to provide more meaning types where you can or add explicit any .
The compiler will no longer infer types in your components / functions.
function fetchData(arg) < return fetch(arg) >// Error: arg has an implicit 'any' type function fetchData(arg: string)
Depending on if you setting your skipLibCheck you may need to import types for your dependencies at this stage as well.
Enable Strict Mode
This last phase will most likely need to happen incrementally. Each team and application will have different needs as far as what how strict you set your compiler.
You can also extend @typescript-eslint/parser for type specific linting.
In conclusion, the TypeScript compiler is your friend. It has not always felt that way when I am in the middle of converting large amounts of code to TypeScript in the past, but I certainly miss it when I am working in a vanilla JS codebase. Also, the TypeScript documentation is a great resource. You can find helpful guides to assist you in migrating to TypeScript or to learn the language for the first time.
Sign up for more like this.
Rust Wasm on Fastly [email protected]
Recently I’ve been playing with a lot of different versions of WebAssmbly using Rust. From Leptos, Dioxus, and most recently by using Compute Edge on Fastly. In this blog, I’m going to build a simple api, and show how we can deploy it to Fastly using their Compute cli. To
Fast and Efficient AWS Lambdas Built With Rust
As Barstool’s resident Rustacean, it is my obligation to push for the use of Rust, and to lament about its elegance and power. When running services at scale, my favorite solution is just ‘throw it in a lambda’, and let AWS worry about handling request load. As for other ways
A Real World Comparison between Cloudflare Workers and Fastly [email protected]
An end-to-end comparison of the two hottest edge computing platforms.
How to Migrate a React App to TypeScript
When I first started learning TypeScript, one of the suggestions I often heard was, “convert one of your existing projects! It’s the best way to learn!” Soon after, a friend from Twitter offered to do just that — show me how to migrate a React app to TypeScript.
The purpose of this article is to be that friend for you and help you migrate your own project to TypeScript. For context, I will be using pieces from a personal project which I migrated while going through this process myself.
The Plan
To make this process feel less daunting, we’ll break this down into steps so that you can execute the migration in individual chunks. I always find this helpful when taking on a large task. Here are all the steps we’ll take to migrate our project:
- Add TypeScript
- Add tsconfig.json
- Start simple
- Convert all files
- Increase strictness
- Clean it up
- Celebrate
NOTE: the most important step in this whole process is number 7. Although we can only get there by working through them in sequential order.
1. Add TypeScript to the Project
First, we need to add TypeScript to our project. Assuming your React project was bootstrapped with create-react-app , we can follow the docs and run:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
yarn add typescript @types/node @types/react @types/react-dom @types/jest
Notice we haven’t changed anything to TypeScript yet. If we run the command to start the project locally ( yarn start in my case), nothing should be different. If that’s the case, then great! We’re ready for the next step.
2. Add the tsconfig.json
Before we can take advantage of TypeScript, we need to configure this via the tsconfig.json . The simplest way for us to get started is to scaffold one using this command:
We have not yet interacted with TypeScript. We have only taken the necessary actions to get things ready. Our next step is to migrate a file to TypeScript. With this, we can complete this step and move onto the next.
3. Start with a Simple Component
The beauty of TypeScript is that you can incrementally adopt it. We can start with a simple component for our first piece of this migration. For my project, I’m going to start with a Button component that looks like this:
import React from 'react' import buttonStyles > from './Button.styles' const Button = ( onClick, text >) => ( button onClick=onClick> className=buttonStyles>> text> button> ) export default Button
To properly convert this, we need to do two things:
Since this component takes two props, we need to change a few things:
import React, MouseEventHandler > from 'react' import buttonStyles > from './Button.styles' type Props = onClick: MouseEventHandler, text: string, > const Button = ( onClick, text >: Props) => ( button onClick=onClick> className=buttonStyles>> text> /button> ) export default Button
Let’s double-check that things are still working by running the project to ensure we didn’t break anything. Notice, here react-scripts will automatically detect the new changes and modify our tsconfig.json for us! Voila! How beautiful is that?
And if all is well, our project will remain in working condition. Give yourself a pat on the back! You’ve successfully migrated your first file to TypeScript. If we wanted to stop here, we could, but let’s push forward.
4. Convert All Files
The next step is to do what we did for step 3, but for all files in the project. If the project you’re migrating is rather large, I suggest doing this over multiple iterations. Otherwise, you may tire yourself out.
During this step, you may need to add additional packages depending on what third-party libraries you’re using. For instance, I am using moment so I had to run yarn add -D @types/moment to add the types as a devDependency .
Here are some other things to keep in mind as you do this:
- Suppress TypeScript errors by adding // @ts-ignore on the line before the error
- If a file uses jsx (i.e. ), the file extension must be .tsx instead of .ts
- Run the project locally to make sure things are still working (they should be)
After you’ve completed this step, the hard stuff is done! Our project will be in TypeScript, but we’ll need to increase the strictness to take advantage of the benefits.
5. Increase tsconfig.json Strictness
Now we are ready to increase the strictness by enabling stricter rules in our tsconfig.json . Thankfully, react-scripts will inform us of any type errors while running our project locally. We will follow the process like so:
And we will repeat this process for the following rules:
- «noImplicitAny»: true
- «strictNullChecks»: true
- «noImplicitThis»: true
- «alwaysStrict»: true
I want to share a tip. If you find that something implicitly has the type any and you’re not sure how to fix it in that moment, don’t. Create this and use it to hush the error:
export type FixMeLater = any
Our goal is to move forward quickly and go back later to fix these.
This will bring a greater amount of type safety to our project. If you’d like to read more about compiler options, you can read about it in the TypeScript Handbook.
Once we’ve done this, we can then replace these:
- «noImplicitAny»: true
- «strictNullChecks»: true
- «noImplicitThis»: true
- «alwaysStrict»: true
which also enables these strict options:
- strictBindCallApply
- strictNullChecks
- strictFunctionTypes
- strictPropertyInitialization
At this point, we have reached a standard level of strictness in our project. If we want to add additional checks, we can add these rules:
- «noUnusedLocals»: true
- «noUnusedParameters»: true
- «noImplicitReturns»: true
- «noFallthroughCasesInSwitch»: true
Once we’ve reached a level of strictness that we’re happy with, we can proceed to the next step.
6. Clean Up Shortcuts
If you added @ts-ignore or took advantage of a FixMeLater type, now is the time to go back and fix them. We don’t have to do these all at once, or ever, but this would be the last step to ensure maximum type safety across your project.
Sometimes the effort to fix these is not worth the time, and other times it is. You’ll have to discuss with your team and decide what makes sense.
7. Celebrate
We did it! We officially migrated our project to TypeScript. Take a moment to celebrate your work. It was certainly not a trivial task. Especially if you were working in a large codebase.
Things to Remember
As we reflect on our efforts, here are some things to remember when migrating a project from React to TypeScript.
Start Small
Take advantage of TypeScript’s ability to gradually adopt it. Go one file at a time at your own pace. Do what makes sense for you and your team. Do not try to tackle it all at once.
Increase Strictness Over Time
There is no need to start with maximum strictness from the beginning. It is a journey. Increase the level as you have time. Eventually, you will reach a level that feels comfortable. Do not feel bad if you do not have 100% strictness. Some type safety is better than no type safety.
Lean on Shortcuts
The @ts-ignore and the tip for FixMeLater are there to help lessen the burden of the migration. Not everything needs to changed at once. Use the shortcuts as you need but do not feel bad for using them. Again, the point is to migrate, but it should not be painful. Over time, you can prioritize replacing these things with proper type safety. But remember, these tools are at your disposal so use them.
This is not the only approach for migrating React projects to TypeScript. However, it is what works for me. I hope it helps you as much as it helped me.
Further Reading
Special thanks to Karl Horky who reached out explaining that the `React.FC` type is not recommended because it provides almost no benefit and it has some downsides. See this GitHub discussion for more information.
Share This Article
Joe Previte is a developer, a teacher and creator of Vim for VSCode. In his spare time, he enjoys creating videos for egghead, writing articles relating to coding and leading an online meditation study group.