Convert a React Native Project to TypeScript in 10 Minutes

  • Alex Hinson
  • July 18, 2019

Alex Hinson presented the following as a Lightning Talk at the 2019 Chain React conference, which was incredible, on July 11. 

Intro

Hey everyone! Thanks for coming out today. I’m Alex and I’m a developer at a great company called Airship based in Birmingham, Alabama.

I want to first start off on a more personal note by saying how thankful I am to be here today. I’ve attended Chain React for the past 2 years, and I am completely honored to be able to present this year. Over the course of those 2 years, a lot has changed in the React & React Native community, particularly regarding TypeScript. As I’m sure everyone in this room can attest to, it has been virtually impossible to browse twitter or blogs and NOT see people talking about switching to TypeScript. You’ve probably seen things like this:

TS Twitter

If you’re like me, you get just a little bit of FOMO anytime these people tweet about something you’re not currently doing in your work. Fortunately, at Airship we’ve moved a handful of projects over to TypeScript, and along the way we found a repeatable, compartmentalized pattern to approach this type of conversion from regular JavaScript to TypeScript.

What is TypeScript

If you aren’t quite as familiar with what TypeScript is, we don’t really have the time to go into any sort of detail today, but essentially, TypeScript is a superset of JavaScript, so it is built on top of the JavaScript you already know, but it just has some additional features that make it unique, such as types.

This close relationship between JavaScript and TypeScript is what makes the conversion process more manageable than you might think.

Conversion

Throughout the process today, we’ll use an existing project of mine, which may be a bit arbitrarily simple, yet… strangely important? :), but nonetheless this project will help convey the ideas involved in each part of the conversion.

One of the great things about TypeScript is that it is very configurable, so we can make it as strict or loose as we want, in terms of type-checking. A common problem I’ve seen is when people try converting projects over to TypeScript all in one go with a strict configuration, so the initial setup is way more effort than it should be. That’s why we’re splitting it up into 3 particular steps.

Each of these 3 steps could be the “end” of your conversation to TS, and you’ll get a certain level of benefits from each.

We will go from the loosest configuration to the strictest, and you can make your own judgements about what level makes sense for you and your team.

Step 1: rename js to ts / implicit any

And on to step 1! Just Make It Work

Our goal for this first step will simply be to get our code running again without having to make any significant changes to the code itself. We will need to setup a basic config file so that TypeScript can know the particular rules we want it to follow or not follow. As I mentioned before, this will be the loosest configuration of TypeScript.

Basic tsconfig

Here is an example of a standard tsconfig file for React Native, which will live in the root of the project. It has a couple of rules that we’re going to gloss over for the sake of time, but I want to focus on one in particular. Setting noImplicitAny to false will allow implicit anys. These implicit anys are used in TypeScript when it cannot infer a particular type of a variable. We’re allowing these in this first step just to get our project to compile with TypeScript without making any significant changes to the code.

The next thing we need to do will just involve changing the file extensions from .js to .ts and .tsx. One slight gotcha with TypeScript is that anytime you want to use JSX, the file has to end in .tsx, which is a bit stricter than how JavaScript works.

If you have a larger project that isn’t just a two screen app about cats, manually changing these over could be a bit time consuming, but luckily, there’s an excellent appropriately titled tool called js-to-ts-converter that handles changing all the file extensions over as well as parsing the file for any JSX and naming the file with a .tsx extension in that case.

Once we’re done changing the file extensions over, we’re now working with a TypeScript project! However, there’s a decent possibility that we might still see a few errors, which is actually a good thing!

In my example project, I was really rushed for time. The investors were calling nonstop, “We need this cat app right now!”, and I was like, “I need to write more tests!”, and they were like, “nooo we must have the cats!”, so I had to rush it to the App Store immediately. Unfortunately, I had a few fundamental JavaScript errors that went unnoticed before, but TypeScript is now able to identify those areas immediately and give me some feedback on what the problem is.

You can see in the example here that I have a logError function at the top that expects 2 arguments, however when I’m using that function below, I’m only passing it one, and TypeScript brought it to my attention immediately and tells me what argument I’m missing.

Once I fix those issues, the project should run successfully, and we’re officially done with step 1!

Step 2: explicit any

Now for step 2 – Be Explicit!

The next step in the process will be to only allow explicit anys, by simply toggling noImplicitAny to true. This means that instead of TypeScript defaulting to an ‘any’ type when it can’t use type inference, it will actually give a compile error. All of the places that the compiler was automatically adding an any type will now require you to give it a type or explicitly say it is an any.

You can see here that I’m saying that this function expects an image ID as a string and a value as a boolean.

I would then need to go through all areas of my application and do this same sort of thing

This is also the time to start creating the common object types that are used throughout the project and adding them where possible.

Here is a Cat object type, and you can see it all the properties that it has. I’ll then use this type all over my project anytime I’m dealing with a Cat in the data.

Another part of this step will be to bring in types from 3rd party libraries that don’t provide TypeScript types. If a library is written in regular JavaScript, we can still add types on top of it that describe its’ usage. There is an excellent open source resource called Definitely Typed that contains types for many popular libraries, such as Lodash, React, or React Native itself, for instance. You just need to add @types before the library name and add it to your project’s dev dependencies.

This step will probably take just a bit longer than the 1st step, but the efforts you spend will pay off dividends once you start adding to your codebase, and especially when you start refactoring.

Like I mentioned before, any of these steps can be the “end” of your TypeScript conversion. We have many projects at Airship that ended up somewhere in the middle of steps 1 and 2, and they are much better off because of it.

However, if you want to keep pushing on and go all in on TypeScript to get the full benefit, there’s only one more step!

Step 3: Strict mode

And we’ll call it Strict Mode!

The last step involves enabling the “strict” setting, which simply adds a number of strict rules for TypeScript to adhere to. Even though this setting might sound a bit invasive and over-the-top, these rules really just enforce good JavaScript patterns in your code.

For example, one of the rules enabled with the “strict” setting is “strictNullChecks”. This was created to help ensure that you don’t try to access a particular property of an object, for instance, that could potentially be null or undefined.

For example, we have this Cat type from earlier and it has an optional property age. You can see that it is optional because it has a question mark after the key, which means that it could be undefined. When I try to run the toString method on age, TypeScript will give me an error, so I’ll need to make sure age exists first before trying to run the method, otherwise I’ll just return a default string.

Depending on the project, there might actually not be that much refactoring for step 3 at all! If you are already writing safe JavaScript, then there’s a high likelihood that TypeScript will agree as well.

Review / Conclusion

And there we have it! We’ve taken a standard JavaScript React Native project and successfully converted it to TypeScript in 3 contained steps.

First we added a basic TypeScript configuration and simply changed the file extensions. Then we got more explicit about the types we are using throughout the application by turning off implicit anys and creating our own common types as well as bring in 3rd party types. And finally we enabled the strict configuration, which further ensures that we’re writing safe and error-resilient code.

Now, I know the title of this lightning talk might have been a bit misleading, because a full & complete conversion to TypeScript with a robust project that isn’t a two screen app about cats could take days or weeks or even months, but I want to reiterate the fact that TypeScript can add value to any project, whether it’s just at step one or 100% integrated.

I hope this quick talk helped clear up some questions you might have had, but there are also many details that we weren’t able to get to today, so please come find me afterwards if you’d like to chat any further about TypeScript and React Native.

To learn more about Alex’s other React Native speaking events across the globe, check out this page. To read more about Alex, check out his bio!

Photo courtesy of the talented @SteveSaul82 (tweet embedded in article above)