React-Native with Typescript

Part 1 - Creating new React-Native app│Story 05: Adding Typescript

--

[Updated August 07, 2021]

In the previous posts of this Part-I of the series, we initialized a new react-native project, added icons for the app to iOS and Android folders as required for publishing the app to respective stores and also configured semantic versioning for the app releases.

In this post, we will configure the project to allow us to code our app in Typescript.

You can skip this story/post if you don’t wish to add Typescript for your app development to go to part II of the series to read the steps for manually releasing the app to Google Play store.
Or to Part-III to if you already have a manual release created for the app on Google Play console and are looking to see how to automate the app’s bundling and release using Gitlab CI-CD pipeline and GPP.

Adding Typescript to a react-native project - Template options

Both Expo and React-Native CLI provide templates to have your app project initialized with Typescript like below:

However I somehow don’t find the folder structure scaffolded by these templates very convenient and prefer to add typescript in the usual way instead of using templates.

Adding Typescript manually with no templates

Install dependencies

On your terminal/command prompt window, with pwd as project’s root folder, start with below commands

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

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

Configuration

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

(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 here:

👆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 75–85): any JS files or folders that you want Typescript to ignore can be added here.

Create 𝚜𝚛𝚌 and 𝚋𝚞𝚒𝚕𝚍 folders

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

📁 Create a folder src under project’s root folder.
📂 Create a folder build under project’s root folder.
📂 Create a folder App under src folder.

When we created the project with the react-native cli’s init command (react-native init your_project_name), it added an App.js under our project’s root folder.

📄 Move App.js file to the src/App folder we created
- Rename App.js to App.tsx.

We would maintain the unit tests of our react components in the same folder of the component.
When react-native’s init command scaffolded the project, it created a __tests__ folder under project’s root folder and the App components test file (App-test.js) under it; we would move this App test file to same folder where we store App.tsx file i.e. under src/App/ folder and rename it to App.spec.tsx. Remove the __tests__ folder from under the project’s root folder.

With above changes, update the file path of App.js in the respective import statements in <<project root folder>>/index.js and src/App/App.spec.tsx as shown in below screenshot 👇

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

Click over image to zoom

Defining React$Node type for typescript

In the initial scaffolded version of App.js, that we have now renamed to App.tsx, you would notice it uses the Flow type Node (or in previous versions React$Node ) like below, which won’t work with typescript.

(click over image to zoom)

Let’s define React$Node type for our typescript app.

Add a folder, TypesAndInterfaces under src. Add a file AppTypes.ts under this folder with below code:

Update 𝙰𝚙𝚙.𝚝𝚜𝚡

Now, update App.tsx as below: (the initial contents scaffolded App.tsx may vary based on the ReactNative version you have)

👆 we removed the import statement for Flow type {Node} import type {Node} from 'react'

line #11: imported the typescript type React$Node from AppTypes.ts that we created above.

line # 21–24: defined type for the Section component’s prop types

line # 26: updated the parameter type and return type for the Section component’s function

line # 52: updated the return type for the App component’s function

line #70–72: added a new section to the screen to indicate the app is now using Typescript

𝚝𝚢𝚙𝚒𝚗𝚐𝚜.𝚍.𝚝𝚜

Based on your React-Native version, you may or may not see a global variable used in your App.tsx. Typescript will throw an error for this global variable being not found.

Such variables can be declared in a file typings.d.ts , that typescript would refer to when transpiling the code.

Add a file src/typings.d.ts and add the declare var global: any to it.

(click over image to zoom)

With above declaration of global in src/typings.d.ts, the error for global variable being not found in App.tsx should be gone now 👇

(click over image to zoom)

(Note if you are getting below errors, try rm -rf node_modules && rm -rf yarn.lock && yarn install. If the error still exists, this might be fix for your issue 👇)

Add a few more scripts in 𝚙𝚊𝚌𝚔𝚊𝚐𝚎.𝚓𝚜𝚘𝚗

When we created the project with react-native cli’s init command, the package.json file it created in the scaffolded project folder contained these scripts:

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

Add above scripts to package.json’s scripts section. These are explained below:

Running updated app on Android and iPhone:

and/or run on android running below on another terminal window:

(click over image to zoom)

(Sometimes you may run into variety of issues when running on iOS/Android based on your system configuration/settings and version compatibility between the resources. Googling for those should find you the fixes)

(I first got below error for iOS)

https://stackoverflow.com/a/63538365 — didn’t work

Next, I tried the command npx react-native run-ios, and it successfully built and ran the app on the iOS emulator device:

After this, I tried removing the app from the emulator and using our buildRunIOS command again. This time the command worked without any issues:

If everything goes fine, the app should build successfully and load in the emulator/phone:

Configure Jest unit tests to use Typescript

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

Add a new file jest.config.js under the project’s root folder like here:

👆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 𝚓𝚎𝚜𝚝.𝚌𝚘𝚗𝚏𝚒𝚐.𝚝𝚜 to 𝚝𝚜𝚌𝚘𝚗𝚏𝚒𝚐.𝚓𝚜𝚘𝚗’s exclude array

Add jest.config.ts to tsconfig.json’s exclude array for Typescript to ignore this file. (you may already have it in the excluded array if you copied it from the tsconfig.json gist provided above.)

Remove jest settings from 𝚙𝚊𝚌𝚔𝚊𝚐𝚎.𝚓𝚜𝚘𝚗

Now that we have the jest configuration in a separate config file for jest, remove its configuration from package.json.

Also, update the test script in package.json to run jest with the above created config file:

Configure 𝚝𝚜-𝚓𝚎𝚜𝚝 to handle synthetic imports

React-Native 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:

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

In tsconfig.json, make sure you have "allowSyntheticDefaultImports": true under compilerOptions.

Adding a new component - 𝚂𝚎𝚌𝚝𝚒𝚘𝚗𝙷𝚎𝚊𝚍𝚎𝚛, and its unit test, coded with Typescript

📂 Create a folder components under src, we would store subfolders for each of our app’s components under this folder.
📂 Create a folder SectionHeaderunder src/components,
Under src/components/SectionHeader, create a file SectionHeader.tsx as below 👇

(click over image to zoom)

Add unit test for 𝚂𝚎𝚌𝚝𝚒𝚘𝚗𝙷𝚎𝚊𝚍𝚎𝚛 component

Under src/components/SectionHeader, create a file SectionHeader.spec.tsx as below:

(click over image to zoom)

Add the 𝚂𝚎𝚌𝚝𝚒𝚘𝚗𝙷𝚎𝚊𝚍𝚎𝚛 component to our App component

Update the src/App/App.tsx file to use the SectionHeader component that we created above 👇

Gist
(click over image to zoom)

👆 Note line# 37–39 above: We added SectionHeader component to our App component.
line # 19: importing the SectionHeader component
Also, line# 81–83, we added style for the SectionHeader container.

Running the App updated with 𝚂𝚎𝚌𝚝𝚒𝚘𝚗𝙷𝚎𝚊𝚍𝚎𝚛:

If you had the app already running on the devices/emulators, it should automatically refresh the view, else run the build commands for iOS and Android to run the app on respective devices/emulators.

The App should now have our new SectionHeader component:

Configure typescript and git to ignore unit tests coverage folder

📌 Add coverage to the exclude array of tsconfig.json
📌 Add /coverage to your .gitignore

Running the unit tests

Run the test and make sure our typescript unit tests are running as expected: yarn test or yarn test --coverage.

(click over images to zoom)

Super! Now we can code our react-native app with Typescript. 🎉 Happy typing React-Native!

Next, we will quickly set up ESLint and Prettier in our project for Code formatting and linting, before moving on to the Part II of this series (Manual set up of the app’s first release to Google Play console)

Prev: Icons, Semantic versioning🏠Next: ESLint and Prettier

--

--