React Native with TypeScript

Complete guide to convert a React Native project to TypeScript

TypeScript is a superset of JavaScript which primarily provides optional static typing, classes and interfaces. Since 2012, TypeScript has been a popular choice for programmers coming to JavaScript from more structured languages.

One of the benefits of TypeScript is better tooling. TypeScript enables IDEs to provide a richer environment for spotting common errors as you type the code (vs running a tool after you’ve finished the code). Also you get to use Visual Studio Code — a performant editor that comes with in-build integration for TypeScript. For a large JavaScript project, adopting TypeScript might result in more robust software, while still being deployable where a regular JavaScript application would run. Backward compatibility with the JavaScript versions is another advantage of using TypeScript.

React world is familiar with Flow — an alternative type checker. Flow develops a deeper understanding of your code and even does inter-procedural analyses. On the other hand, TypeScript provides great tooling and language services for autocompletion, code navigation, and refactoring.

Enough with the advantages! The advantages of the tools used in this project verses the alternatives (TypeScript vs Flow, Visual Studio vs Atom, Babel vs TypeScript) are beyond the scope of this article. I’m assuming that you are here because you already know the benefits of TypeScript. However I will be leaving some useful links towards the end of this article.

This guide will take you through the steps required for setting up a React Native project in TypeScript.


Setup React Native

We will start with the same steps as in getting started with a non-TypeScript React Native project. Here I’m going to describe the minimal steps because React Native’s Get Started Guide is well written and available here.

# install node and watchman
brew install node
brew install watchman
# install react native cli
npm install react-native-cli -g

Install XCode (for iOS) and Android Studio (for android).

Create Project

Easy and effective way to start with a react-native project is to use CLI. The command below will generate a project with all the setup required for development including babel, iOS and Android setup. This is not in TypeScript yet, but don’t worry, we will convert it soon. Choose your own name for the project; Todo is the name I prefer for this tutorial.

react-native init Todo

Setup Visual Studio Code

Visual Studio Code is one of the best editors out there, especially for TypeScript projects. Since we want to convert our project into TypeScript, let’s go ahead and install VSCode. You can skip this section if you are already familiar with VSCode setup.

Download Visual Studio Code and install. Open Visual Studio Code and press Cmd+Shift+P and type Code. Select the command Shell Command: Install 'code' command in PATH.

We need to configure TSLint extension to be able to do static checks from within the editor. Install it from the extensions tab.

Configure iOS & Test

Before we start converting the project to TypeScript, we need to be sure that our setup works. I’m going to setup iOS and test. You could alternatively try android, especially if you are trying this tutorial from Windows (see Refactor Android to TypeScript section).

Let’s open the project in VSCode. Go to the project folder Todo and type code .;

Update package.json

{
"scripts": {
"start:ios": "react-native run-ios"
}
}

That’s all for now, but we will come back and add more later. Let’s run and see if everything is fine.

npm run start:ios

It’s good to enable Live Reload or Hot Reload . In the emulator, press Cmd+D to get the developer menu. To test if your changes are being reflected, open index.ios.js, change a text, save and see if that’s reflected in the emulator.

And finally, if everything is fine, let’s save the work-so-far into a git repository. VS Code has nice integration with git; so open Git Tab and hit Initialize Git Repository. Give a comment and commit.

Setup TypeScript

Now comes the fun part. Let’s configure TypeScript for this project. We will start this by installing TypeScript, linter and some other required libraries.

npm install typescript tslint rimraf concurrently --save-dev

Create tsconfig.json at the root of the project with the following configuration:

We also need typings for react and react native (typings are definition files — read more about it here)

npm install @types/react @types/react-native @types/jest --save-dev

Test this setup: create src folder and a file test.ts; copy and paste the below content:

export function helloWorld()
return "Hello World"
}

Let’s add more scripts to package.json.

{
"scripts": {
"tsc": "tsc",
"clean": "rimraf artifacts",
"build": "npm run clean && npm run tsc --"
}
}

Now running npm run build should create a folder artifacts at the root folder. If you see artifacts folder and test.js file under it, we have successfully setup TypeScript. And remember to add artifacts to .gitignore file. We don’t want the content of this folder to be added to source control, because our true source will soon be in TypeScript.

Setup Linter

The whole purpose of converting this project to TypeScript, is to get proper linter errors as we type. Therefore this step is very important. To add TSLint support, create a file tslint.json at the root of the project with the following content:

Feel free to change these values as per the coding standards you may have defined. Now let’s add linter script to package.json

{
"scripts": {
"lint": "tslint src/**/*.ts"
}
}

Open src/test.ts you must see an error; this is an indication that the linter started working.

If you don’t see this error, the reason could be one of the following:

  1. VS Code needs a restart
  2. TSLint extension (explained under Setup Visual Studio Code ) is not installed

Also run the command at terminal: npm run lint. The command should fail with the following error — as expected.

Change "Hello World" to 'Hello World' (with single quote), and observe the error disappearing.

Refactor iOS to TypeScript

Move index.ios.js to src folder and change its extension to tsx. And change the content:

Go ahead and run npm run build and observe that index.ios.js file is generated under artifacts folder. However if you check the emulator, you will see an error:

To fix this problem, open ios/Todo/AppDelegate.m and change "index.ios" to "artifacts/index.ios".

jsCodeLocation = [[RCTBundleURLProvider sharedSettings]] jsBundleURLForBundleRoot:@"artifacts/index.ios" fallbackResource:nil];

And update package.json

{
"scripts": {
"watch": "npm run build -- -w",
"start:ios": "npm run build && concurrently -r 'npm run watch' 'react-native run-ios'"
}
}

Terminate the npm run start:ios and run again. Remember to terminate packager as well (running in a separate terminal window). Now everything should be back to normal. To test if your changes are being reflected, open src/index.ios.tsx, change a text, save and see if that’s reflected in the emulator. If yes, we have successfully migrated iOS code to TypeScript.

Refactor Android to TypeScript

The same steps: move index.android.js to src folder and change its extension to tsx. And change the content:

..
interface Props { }
interface State { }
export default class Todo extends Component<Props, State> {
render() {
....
}
}
const styles: any = StyleSheet.create({
....
})

Go ahead and run npm run build and observe that index.android.js file is generated under artifacts folder. However if you check the emulator/device you will see an error just like in iOS.

To fix this problem, open android/app/build.gradle and add the following line before apply from “../../node_modules/react-native/react.gradle”

project.ext.react = [
entryFile: "artifacts/index.android.js"
]

And open MainApplication.java and add the following method under ReactNativeHost:

@Override
protected String getJSMainModuleName() {
return "artifacts/index.android";
}

And finally add start:android script to package.json

{
"scripts": {
"start:android": "npm run build && concurrently -r 'npm run watch' 'react-native run-android'`
}
}

Now run npm run start:android. Make sure to connect an android device or run an emulator before this command. adb devices will let you test if a device is connected or not.

Setup Test Suit

Test Suits are important to your project. It’s necessary to convert them to TypeScript to get the best of both worlds. To start with, move index.ios.js from __tests__ folder to src folder and rename it to index.ios.spec.tsx. The content must be:

import 'react-native'
import Index from './index.ios'
import React from 'react'
import renderer from 'react-test-renderer'
it('renders correctly', () => {
const tree = renderer.create(
<Index />
)
expect(tree).toBeDefined()
})

Update package.json with the following jest configuration:

{
"scripts": {
"test": "jest --coverage"
},

...
"jest": {
"preset": "react-native",
"testRegex": "artifacts/.+\\.(test|spec).js$",
"coverageDirectory": "coverage",
"coverageReporters": [
"text-summary",
"html"
],
"collectCoverageFrom": [
"artifacts/**/*.js",
"!artifacts/**/*.spec.js",
"!artifacts/**/*.index.js"
]
}
}

Run npm test. This should now run index.ios.spec and you must see the coverage report in console. More detailed report in HTML format can be found at coverage/index.html

Repeat the same for index.android.js as well.

And finally

We have a react native project in TypeScript. The final package.json will look like this:

Remember to remove the unnecessary files and folders.

rm -rf __tests__
rm -rf .flowconfig
rm -rf src/test.ts

Hope this article was helpful to you. Please make sure to checkout my other projects and articles. Enjoy coding!