SERIES: React Native (Step by Step) — Working with Typescript and Linting

In the first part of our React Native ( Step by Step) series, we will look at how to start a new project with Expo and Typescript, configure our linter and talk a bit about the how and why. Grab your coffee, relax and strap in for a fascinating journey.

As always, you will find the finished code linked on GitHub at the end of the article.

SERIES: React Native (Step by Step)
1) => you are here <=
2) React Redux + Toolkit with Typescript
3) Strongly-Typed Navigation with React Navigation 5.x and Typescript

Typescript — WHY?

I will not start a long-winded discussion about the pro’s and con’s and keep it short. I’ve decided to work with typescript in my private projects for a lot of reasons.

  • I am working with typescript in my 9 to 5 job on a regular basis
  • I like the additional information, my IDE can draw from strongly typed code
  • Many of my reusable code bits will at some point be put into separated repositories and distributed via NPM

I, too, have had my fair share of “Duh” moments when running into issues with stuff my compiler/linter did not like because I’ve been too vague or simply had not typed something at all because it looked too complex and had hindered me at the time I was writing it. I’ve adopted a routine that allows me to // @ts-ignore something for the time being and running a check before I commit my changes, making sure I think twice before allowing an unchecked piece of code into my repositories.

There are moments when you might puzzle about WHY the typescript compiler does not want to allow a certain piece of code or accept a type that looks like it should be ok and I’ve come to the realisation that in most cases I was about to do something that COULD go wrong at some point in the future and bite me in my backside. Yeah, sure. I, as the developer, know fully well that I will never call this function at a point where my props are still undefined… or do I? Adding null checks (and other safeguards) has by now become a habit I am happy to live with. I’ve seen enough times in enterprise projects where something breaks because, for some unforeseen reason, a server response was empty or had a missing or wrong type. It was not our code that was wrong but putting too much trust in the contracts of APIs and not making sure on both ends that things are as expected can lead to some nasty and sometimes hard to track down bugs.

Long story short: I’ve learned to stop worrying and love the compiler. I’ve learned a lot by trying to understand WHAT typescript was trying to tell me and I think it made me a better programmer by NOT TRUSTING the Typescript Compiler to prevent everything.

Typescript — HOW?

I assume you have worked with NPM and GitHub in the past and at least know the basics. To get started with React Native using EXPO you need to install a command-line tool called expo-cli and it is recommended to install it globally using the -g flag. At the time of writing, I’m using expo-cli@4.0.17 but there won’t be any breaking changes for later versions of 4.x when you are reading this in the future.

npm install -g expo-cli

Using Expo to initialise our new project, we have to typescript templates to choose from. For our case, we will build everything from scratch step by step so we will choose the empty typescript template from the managed workflow. The following command, when entered from your desired location via command line, will then create a new folder by the name you provide in the init and start populating it with the template files you choose from the following menu.

expo init yourProjectName

It might take a moment for the expo-cli to download and install all dependencies from npm but once it’s done, you can simply cd yourProjectName into the new folder and start working.

That’s it, you did it. You successfully started and configured a React Native project via Expo that runs with typescript.

While it might not feel like we did a lot so far, I can only recommend syncing your work to a versioned repository service like GitHub, GitLab or whatever floats your boat. I use GitLab at work and GitHub for both open source and private projects.

This article on shows the whole process in a very clean and easy to follow explanation so I won’t repeat that.

If you want to make sure, everything so far is working as intended, simply enter the following line into your command-line tool while located at the project root and you should see the following page opened in your default browser. To check the actual mobile app, you can start an emulator from the left-hand menu, for mac users with Xcode, the iOS simulator should work best.

npm start
Expo Web Interface

Typescript — Additional Setup

This is usually the first thing I copy from my other projects because it makes working in most IDEs so much easier. A .editorconfig file at the root level of your project folder can be read by most IDEs by default while others can be "enabled" by installing a small plugin. The IDE will then automatically help you with correct indentations, mark the max line length and more.

Go to this site, if you want to know more or check your own IDE for plugins. Don’t be put off by the 90’s comic style website. Their content is state of the art.

Here’s my current config, feel free to add it to your projects too.

Linting and Prettier


npx install-peerdeps --dev eslint-config-airbnb

This will install the following packages to your projects dev dependencies:

  • eslint@7.2.0
  • eslint-config-airbnb@18.2.1
  • eslint-plugin-react@7.22.0
  • eslint-plugin-import@2.22.1
  • eslint-plugin-react-hooks@4.0.0
  • eslint-plugin-jsx-a11y@6.4.1

linting typescript

npm i --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

prettier linting

npm i --save-dev prettier eslint-config-prettier eslint-plugin-prettier

To run you through the basics here, eslint is the actual linter we will be using while eslint plugin import is used to extend the linter with additional functionality. The Typescript parser is needed to parse our typescript code so eslint can do its job and prettier will allow us to automatically fix/change some of the code issues according to our predefined rules.

To get a basic setup, you could simply install eslint and run eslint --init to start a guided init process but we will do that ourselves and add our own .eslintrc config file to the project, again at root level.

Let’s have a look at our finished config file.

Let’s have a quick look at the different parts of our config file:

  • “extends” => our presets of rules
  • “parser” => points at our typescript parser
  • “parserOptions” => as the name implies
  • “plugins” => some plugins for our convenience
  • “rules” => this is where we add our own rules and overwrite presets from the “extends” section that we disagree with

The last thing we need to add is our .prettierrc config file. There are only a few minor adjustments we need to make here because most of the things we want to be fixed are already covered by our eslint rules. Simply add a new .prettierrc file at root level with these 5 lines.

If you want to be adamant about clean code in your repository, you could go one step further and run the linter before accepting new code as a commit. I’m not working with a pre-commit hook that completely prevents pushing code with linting errors into the repository. This might be the right thing for you and I can only recommend to you to have a look at husky for this but as I’m doing a lot of prototyping in my projects and my workflow includes code reviews before I merge anything into my development or main branches, I’m not restricting this.

To run our linter and make use of the prettier auto code corrections, we need to add two new scripts to our package.json.

Both commands run the typescript compiler without making changes to the code (tsc --noEmit) and then run eslint on all matching files starting at root level.

One word of warning/advice on my scripts here. I’m piping all erroneous output away (|| true) because the script will return an error when running and finding linting errors. This is the expected behaviour and very useful when you chain commands. Do not add “|| true” at the end, if you plan to use these scripts in combination with others.
I personally do not like to see 10 lines of useless “npm ERR!” every time I run my linter. I want to look at my linting errors in my terminal and determine where I need to fix stuff so this console output has no value to me in this scenario.

Wrapping Up

Running npm run fix will make most of those errors magically disappear. Feels good, doesn't it?

The remaining error is a missing return type so let’s fix this real quick and go home. App() is returning a React Element so we can simply tap into React.ReactElement and add this as the return type of the App Component.


In our next sessions, we will have a look at some basic solutions for navigation, state management and project file structure.

Here is the promised link to the (Pre-)Release tag on Github.

Some words about me:

Here are some of my recent topics:
Spread & Rest Syntax in Javascript
- clean and simple Redux, explained
- Game Theory behind Incremental Games
- Custom and flexible UI Frames in React Native

And if you feel really supportive right now, you can always support me on patreon, thus allowing me to continue to write tutorials and offer support in the comments section.

I’m a Web / App Developer & father 👨‍👩‍👧 doing freelance and part-time agency work since 2003, 💻 building stuff on the side 🕹 and attending conferences 🎟