Boaty: Setting up your development environment


Note: this is part 2 of a series of posts, aimed at building a React Native app from the ground up. If you haven’t already done so, it’s worth starting from the beginning of the series.

In part 1, we set up our local machine to compile and run a React Native app, and scaffolded a new app Boaty.

Now that we have an app to work with, it’s worth spending some time setting up your development environment. This will speed up the development process and ensure that the app is produced with clean code and optimised assets.

In this post we’ll be covering the setup of:

  • TypeScript, to add static typing and autocomplete (IntelliSense) to your JavaScript code
  • TSLint, to ensure that the code is written without any syntax errors or bad practices
  • Prettier, to automatically format the code to a standard coding style
  • ImageMin, for optimising image assets
  • Husky, for automatically checking / formatting code and optimising assets when a git commit occurs

Editor

Before we start configuring the environment, you’ll want to choose your editor. Select one that supports TypeScript, TSLint syntax highlighting and prettier code formatting - I recommend VSCode but you may have another editor which you prefer.

The instructions in the rest of this post assume that you are using VSCode as your editor.

TypeScript

TypeScript is a typed superset of JavaScript. It allows you to write JavaScript code with static types.

There are many benefits to this approach:

  • catch bugs sooner (at compile-time, rather than when your code is run)
  • provide hints to your editor, allowing for autocompletion of code as you type

Note: You may see blog posts and guides elsewhere that use react-native-typescript-transform - this is no longer necessary as of v0.57 of React Native.

As of v0.57, React Native supports TypeScript natively, but we still need to add TypeScript support to our editor:

  • Install the TypeScript compiler locally, and the typings for React and React Native:
npm install -D [email protected] @types/react @types/react-native

Note: we are specifically installing TypeScript v3.0.3 since at the time of writing, the react-native typings don’t fully support the latest TypeScript version.

  • Create a new file tsconfig.json in the root of the project, and add the following contents:
{
  "compilerOptions": {
    // Target latest version of ECMAScript.
    "target": "esnext",
    // Search under node_modules for non-relative imports.
    "moduleResolution": "node",
    // Don't emit; allow Babel to transform files.
    "noEmit": true,
    // Enable strictest settings like strictNullChecks & noImplicitAny.
    "strict": true,
    // Disallow features that require cross-file information for emit.
    "isolatedModules": true,
    // Import non-ES modules as default imports.
    "esModuleInterop": true,
    // Allow `import React from 'react'` style imports
    "allowSyntheticDefaultImports": true,
    // Ensure JSX is compiled using React.createElement
    "jsx": "react",
    // Include the latest version of ECMAScript
    "lib": ["esnext"]
  },
  "include": [
    "src"
  ]
}
  • Create a new folder src in your project.
  • Move the file App.js to the src/ folder, and rename it to App.tsx.
  • Remove the line at the top of this file which reads @flow
  • Change the line which reads type Props = {}; to interface Props {};
  • Open the file index.js and change the import on line 4 to read import App from './src/App';
  • Delete the file .flowconfig (this is used for a competing type system Flow, which we won’t be using).

If you are using VSCode, tell it to use the workspace-local version of TypeScript that we’ve just installed:

  • Open your workspace settings file (.vscode/settings.json)
  • Add the following entry to it: "typescript.tsdk": "./node_modules/typescript/lib"
  • Open your App.tsx file in VSCode. In the statusbar at the bottom of the screen, the TypeScript version number will be reported. Click on it, and ensure that the ‘Use Workspace Version’ option is checked (see here for more details).

To check that the compiler is working correctly, open a terminal in the project root and run the following command:

node_modules/.bin/tsc

If all is well, you should see no errors reported.

Now, in the terminal, run:

npm start

In a separate terminal start the app:

react-native run-ios

The app should load in the iOS simulator as normal.

TSLint

As TypeScript is a statically typed language, it is very good at catching errors in your code. There are some issues however that TypeScript itself cannot catch, such as ensuring your code conforms to best practices regarding readability and maintainability.

This is the gap that TSLint attempts to fill. TSLint is a linting tool for TypeScript - it analyses your code as you type for common readability and maintainability issues, and suggests areas for improvement.

To install and configure TSLint:

npm install -D tslint tslint-react
  • Create a new file tslint.json in the project root and add the following contents:
{
  "extends": ["tslint:recommended", "tslint-react"],
  "rules": {
    "quotemark": [true, "single", "jsx-double"],
    "object-literal-sort-keys": false
  }
}

If you open the App.tsx file you should now see a few errors appear in the editor. These errors are reported by TSLint, and although they are not syntax errors (i.e. your code will still run), they indicate that the code can be cleaned up. We need to make three changes:

  • Remove the interface Props line. Empty interfaces are not needed and add unnecessary noise to the code.
  • Remove the <Props> code after Component (since we are no longer using the Props interface).
  • Add the public keyword before render().

You may not agree with the default rules that tslint ships with - if not, you can override each rule by adding a new item to the rules object in tslint.json. For more information on each rule, check out the documentation.

Prettier

The next tool that we’ll be setting up is Prettier, a formatting tool which automatically rewrites your code to meet a specific coding style.

To install and configure Prettier:

npm install -D prettier
  • Create a new file .prettierrc in the root of your project and add the following contents:
{
  "singleQuote": true,
  "trailingComma": "es5"
}
  • Configure VSCode to automatically format your code when you save:
    • Open the file .vscode/settings.json
    • Add the following entries:
      • "editor.formatOnSave": true
      • "javascript.format.enable": false

To check that prettier is set up correctly, open up your App.tsx file. Remove the semicolon from the end of line 8 and press save: prettier should kick in and automatically add the semicolon back in.

Conflicts with tslint

Now that we have prettier formatting our code automatically, we need to make TSLint aware of prettier, otherwise changes made by prettier may conflict with the TSLint rules. It would also be nice if any prettier warnings were surfaced as TSLint errors, so we can see them in our editor.

We can achieve this by installing two new packages:

npm install -D tslint-config-prettier   # Makes TSLint accept any rules that prettier enforces
npm install -D tslint-plugin-prettier   # Show prettier warnings as TSLint errors in the editor

Now that these packages are installed, make TSLint aware of them by changing the tslint.json configuration to the following:

{
  "extends": [
    "tslint:recommended",
    "tslint-react",
    "tslint-config-prettier",
    "tslint-plugin-prettier"
  ],
  "rules": {
    "prettier": true,
    "quotemark": [true, "single", "jsx-double"],
    "object-literal-sort-keys": false
  }
}

These changes will ensure that prettier and TSLint play together nicely.

Husky

Husky is a useful tool that allows you to run an action prior to code being committed to version control. If the action fails, the commit is aborted. It can be used to prevent ‘bad’ commits.

We are going to use husky to perform two actions before code is committed:

  • format the code with prettier, and lint the code using TSLint, aborting if there are any errors
  • optimise any image assets that have been added to the filesystem

Husky itself is just a tool to run an action when a commit happens. We need to write these actions ourselves in the codebase.

A naive solution could be to run the actions above on every file in the codebase, on every commit. This is wildly inefficient however - files that have not changed do not need to be checked. We therefore need a way of running actions only on files that have been added or changed.

Fortunately, another tool exists to help us with this, lint-staged. This allows us to run a set of commands against files that are in the git staging area, i.e. files that are about to be committed.

By combining husky and lint-staged, we can run a series of actions on the files that have changed when a commit occurs.

Finally, we also need a module to perform the image optimisation - imagemin-lint-staged is the module we’ll be using.

Putting this altogether, install these packages:

npm install -D husky lint-staged imagemin-lint-staged

Create two new files in the root of the project:

  • .huskyrc, with the following contents:
{
  "hooks": {
    "pre-commit": "lint-staged"
  }
}
  • .lintstagedrc, with the following contents:
{
  "linters": {
    "*.{ts,tsx}": ["prettier --write", "tslint --fix", "git add"],
    "*.{png,jpeg,jpg,gif,svg}": ["imagemin-lint-staged", "git add"]
  }
}

Now, whenever you make a new commit, the husky pre-commit hook will be run. This in turn will run lint-staged, which will:

  • run any .ts or .tsx files through prettier, and lint them with TSLint. If there are no errors, they will be re-added to the staging area.
  • run any image files through imagemin, adding the newly optimised image to the staging area.

If all was well, the commit will now contain code which is formatted correctly, with images that are nicely minified.

Summary

We’ve set up our editor to make React Native development much smoother with the following tools:

  • TypeScript, to add static typing and autocomplete to our code
  • TSLint, to highlight issues with code readability and maintenance
  • Prettier, to automatically format our code in a standard way on save
  • Husky and Lint-staged, to automatically format our code and optimise our assets before a commit

The code for this section can be found on the accompanying GitHub repo, make sure to check out the changes to the code from part 1.

Next Steps

You should now have a fully loaded development environment, which serves as an excellent foundation for implementing a React Native app. In part 3, we’ll build the app.


If you’d like to learn more about building an app with TypeScript and/or React Native, I run in-person or remote workshops which walk through the fundamentals of building apps with these cutting-edge tools. If you are interested in learning more, let me know and I’d be happy to chat further!