Building a React Native App using Expo and Typescript (Part 1)

This is the first part of a two-part guide to how to create a React Native App using Expo and Typescript. Here, we’ll create an example RN app, starting with Expo’s create-react-native-app (CRNA), and configure it to develop our RN code and Jest unit tests using Typescript.

1. Create your React Native project using CRNA

Install Expo’s create-react-native-app (CRNA) tool:

$ yarn global add create-react-native-app

Open Terminal, and cd to your working folder of choice. Run the command below to create a new React Native project:

create-react-native-app CRNAExpoTSExample

At this command, CRNA will scaffold a very basic but ready-to-run React Native app for you. Once the app is created, cd to the created app’s project folder, and make sure the basic initial app scaffolded by CRNA is working:

cd CRNAExpoTSExample

yarn start

QR Code generated by running the `yarn start` command in Terminal

Open our example app on your mobile device

Download the Expo Client on your mobile phone (Android or iPhone). Open the Expo Client and select ‘Scan QR code’ option. Make sure your development machine and the mobile phone are on same WiFi network.

Scan the QR code above using your Expo Client (with version 26.0.0, Expo had to discontinue the QR scan feature on iPhone, you can still scan the QR code from iPhone’s camera and it will prompt you to open it using Expo app). Our app should now open on your phone:

Initial React-Native App as scaffolded by CRNA

Running Unit Tests

Also make sure the unit test scaffold by CRNA in the project folder is working by running yarn test :

2. Add Typescript

Install dependencies

Start with:

yarn add typescript tslint -D

yarn add @types/react @types/react-native @types/react-dom -D

We’ll also need rimraf and concurrently to clean the output folder for ts-transpiled-to-js files and concurrently running npm scripts:

yarn add concurrently rimraf -D

For writing Jest unit tests in typescript we will need ts-jest . We’ll also install type defs for Jest and React test renderers:

yarn add ts-jest @types/jest @types/react-test-renderer -D

Configuration

Set up your Typescript config file ( tsconfig.json ) using the tsc command, or simply create the file manually within the project folder.

tsc --init

(If tsc command is failing for you, with an error like tsc command not found , check whether you have Typescript installed globally and /node/bin in your $PATH var — see https://stackoverflow.com/a/37092975 or https://stackoverflow.com/a/37117612 or https://stackoverflow.com/a/46222888) for more information.

tsconfig.json

Open the project in your code editor and update tsconfig.json to something like this:

(Note the settings rootDir and outDir (line #6-7) above.)

We’d like to store all our Typescript app code files in folders/subfolders under the src folder. With “rootDir”: “src , the Typescript compiler will look for all the .ts or .tsx files within the src folder and its subfolders.

“outDir”: “build” means Typescript will output the transpiled Javascript files under build folder.

Also note the “exclude” property above (lines 31–36): any JS files or folders that you want Typescript to ignore can be added here.

Add tslint.json

Add tslint.json file under the project folder copying its content from here.

Create src and build folders

Since we configured src and build as rootDir and outputDir in the tsconfig.json , create these folders under our project’s root folder.

When we created the project with the CRNA command ( create-react-native-app CRNAExpoTSExample ), it added an App.js and App.test.js under our project folder. Move these files to the src folder we created, then rename App.js and App.test.js to App.tsx and App.test.tsx respectively.

At this point, your project folder and files should look something like this:

Add a few more scripts in package.json

When we created the project above with our CRNA command, the package.json file it created in the scaffolded project folder contained these scripts:

...

"scripts": {

"start": "react-native-scripts start",

"eject": "react-native-scripts eject",

"android": "react-native-scripts android",

"ios": "react-native-scripts ios",

"test": "node node_modules/jest/bin/jest.js"

}

...

Now, we’ll add few more scripts to run our tasks to compile Typescript, create the build and start our app:

We’ll add above scripts to our package.json’s scripts section. These are explained below:

The full package.json can be found here.

Adding App.js under project’s root folder

Notice that, in the package.json file that was originally created by CRNA, the “main” entry point for app is set to:

"main":"./node_modules/react-native-scripts/build/bin/crna-entry.js"

This means our app starts from this crna-entry.js file. Open this file, and you’ll find that it references our App.js file:

var _App = require('../../../../App');

This means it’s expecting the app module to be in the App.js file under our project’s root folder. However, we moved the original App.js file to the src folder. Furthermore, the Typescript compiler will output the transpiled ts-to-js file under the build folder.

So for CRNA to work with our changed folder structure and our Typescript configuration, let’s add an App.js file under the project folder that will simply export our App component in src/App.tsx , which the Typescript compiler will output to the build folder.

Create the App.js file under the project’s root folder:

import App from './build/App';

export default App;

Add App.js to tsconfig.json’s exclude array

Add the App.js file we created above to tsconfig.json’s exclude array for Typescript to ignore this file.

3. Run our app

We’re all set now to run our Typescript app. Run the command:

yarn run buildAndStart

This command runs the buildAndStart script we created in our package.json file above: it’ll clean the build folder and output the transpiledTtypescript files to build folder, then start the project. As before, CRNA will generate a QR code on your terminal that you can scan from the Expo Client on your Android/iPhone, and you should see our app running on the device.

Make some changes to App.tsx to see how live reloading works to refresh the app on your device showing the latest changes. (Refreshing may take some time for the first time.)

(If you have git repository for the project folder, add /build to .gitignore, as we don’t need to push build files to git. Also add “build” to tsconfig.json’s exclude array for typescript to ignore the files under this folder.)

4. Configure Jest unit tests to use Typescript

We installed ts-test package to allow us to write our Jest unit tests using Typescript.

Add a new file jest.config.js under the project folder:

In the testRegex setting above, we’re specifying that it should look for any file under __test__ folder or any .test or .spec file under /src/ folder. We’ll put all our development source code under the src folder, and the spec/test file will go in the subfolders under src along with the resource/component.

Add jest.config.ts to tsconfig.json’s exclude array

Add jest.config.ts to tsconfig.json’s exclude array for Typescript to ignore this file.

Configure ts-jest to handle synthetic imports

CRNA uses synthetic default imports in the app component file it creates when scaffolding the project. Thus you’ll see React imported like below in App.tsx :

import React from 'react';

For ts-jest to handle synthetic default imports, make sure you have this configuration:

In the tsconfig.json , make sure you have "allowSyntheticDefaultImports": true under compilerOptions. Make sure you have transformer set to handle the transpiled jsx files with bable-test:

module.exports = {

"transform": {

"^.+\\.jsx?$": "<rootDir>/node_modules/babel-jest",

"^.+\\.tsx?$": "ts-jest"

},

....

....

}

Running the unit test

Run the test and make sure its running as expected: yarn test .

👏 Yay!! We got Typescript configured for developing our Expo / React Native App and unit tests. Here’s the example code if you want to dig in to it.

In part two of this tutorial, we’ll create a very simple Header component and its unit test, and make sure everything works as expected with our Typescript configuration.