Just My Type: Rolling a fresh TypeScript project with Jest, ESLint and Prettier in 2019
Tags: javascript, typescript, beginners, webdev
Published: 2019-07-06 18:19:03

---
title: Just My Type: Rolling a fresh TypeScript project with Jest, ESLint and Prettier in 2019
published: true
cover_image: https://images.unsplash.com/photo-1534190239940-9ba8944ea261?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1189&q=80
description: A quick and easy guide and GitHub template for getting TypeScript up and running. 
tags: javascript, typescript, beginners, webdev
--- 

TypeScript's motto is that it's *JavaScript that scales*, and one area where that's definitely true is in the extra work you have to do in order to spin up a project. Ba-dum! The time investment isn't so bad when you're setting up something you'll be working across for months on end, but when you're looking to just spin up a repo to learn, experiment or solve a few katas it can be a bit of a fiddle to get everything up and running over and over and over again. I just want that beautiful static typing 😭

If you'd like to just skip all the setup and get stuck in, you [can find this starter template on GitHub](https://github.com/martingaston/typescript-skeleton). 

### Install Dependencies

Get ready to install this hearty wedge of dependencies. I'm using `npm`:

`$ npm init -y && npm i -D jest typescript ts-jest @types/jest eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-plugin-prettier eslint-config-prettier`

[TypeScript](https://www.typescriptlang.org/)'s own getting started requests a global installation, but I like to avoid these whenever possible. We'll throw in a script to get access to the TypeScript compiler, `tsc`, elsewhere in our config. 

### Add Configuration

#### 1. `package.json`

Outside of the standard `npm init -y` template, nip into your `package.json` and add a couple of updates to your `scripts` to easily get access to our test runner and the TypeScript compiler. 

```json
  "scripts": {
    "test": "jest",
    "tsc": "tsc"
  },
```

#### 2. `jest.config.js`

We're using `ts-jest` with our project so we can [feast on TypeScript's type checking](https://kulshekhar.github.io/ts-jest/user/babel7-or-ts) directly from Jest. A quick little `$ npx ts-jest config:init` will produce the below: 

```javascript
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
```

#### 3. `tsconfig.json`

By default, the TypeScript compiler can produce a hefty configuration file - it's got really informative comments so it helps the output not get too overwhelming. You can build one by running `$ npm run tsc -- --init`, and the options below are how I like to have mine setup right now: 

```json
{
  "compilerOptions": {
    "target": "ES2015",                       
    "module": "commonjs",                     
    "outDir": "build",                        
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true
  },
  "exclude": [
    "node_modules",
    "test"
  ]
}
```

#### 4. `.eslintrc.js` and `.pretterrc`

As of the start of this year, the TypeScript team formally adopted ESLint as the linter de rigueur of the TypeScript space.

If you're interested, the `typescript-eslint` team talk about the inherent challenge of parsing TypeScript and ESLint together [in their repo](https://github.com/typescript-eslint/typescript-eslint#why-does-this-project-exist). It's a pretty fascinating topic! 

Wedging ESLint together with Prettier - my favourite code formatter - and TypeScript is probably the most involved step. The brief overview is that we configure ESLint to disregard the rules that Prettier is concerned with, then roll Prettier in with our linter so they can run together as a single step.

We also need to point our ESLint config to our `tsconfig.json` file and set up a few other options, such as the `sourceType` and `ecmaVersion` settings.

Here's how that looks:

`.eslintrc.js`
```javascript
module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
    'prettier/@typescript-eslint',
  ],
  parserOptions: {
    project: './tsconfig.json',
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  rules: {
    // Special ESLint rules or overrides go here.
  },
}
```

Over in `.prettierrc` I just like to pop in my preferred options: 

`.prettierrc`
```yaml
trailingComma: "es5"
tabWidth: 2
semi: false
singleQuote: true
```

And now we've got the one-two combo of ESLint and Prettier helping sure our code looks swell and operates properly. We're ready to actually write some code! 

### Hello World!

I like to separate out code in a `src` directory, with tests in `test`. So a quick `$ mkdir src test` will set that up, and our previous steps will have TypeScript transpile everything in our `src` directory to a JavaScript `build` directory when we `npm run tsc`. 

But to really make check everything is working, it's time for a quintessential rite of passage: 

`test/sayHello.test.ts`
```typescript
import sayHello from '../src/sayHello'

test('sayHello can greet a user', (): void => {
  const user = { name: 'TypeScript' }
  expect(sayHello(user)).toBe('Hello TypeScript!')
})
```

And then to make that pass (and to check it's all working) we can throw in some of our fancy TypeScript features: 

`src/sayHello.ts`
```typescript
interface User {
  name: string
}

const sayHello = (user: User): string => `Hello ${user.name}!`

export default sayHello
```

`$ npm test`

```bash
 PASS  test/sayHello.test.ts
  ✓ sayHello can greet a user (4ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.206s
Ran all test suites.
```

Fantastic! We've created a fine little environment for playing with TypeScript. 

And, just to make sure we haven't set up any conflicting rules in our ESLint config, we can run a quick check: 

```bash
$ ./node_modules/.bin/eslint --print-config src/sayHello.ts | ./node_modules/.bin/eslint-config-prettier-check
> No rules that are unnecessary or conflict with Prettier were found.
```

TypeScript is a really fun language to work in, but getting into a place where you can run the config quickly makes it far more pleasurable to get started. Feel free to share your own tips and tricks of configuring your TypeScript environment - I'm still learning the language myself, so would love to know how other people like to setup their repos! 

### Bonus 1: Add a Git Hook

One really neat part about the JavaScript ecosystem is that husky and lint-staged make it incredibly straightforward to run our linters and test suites as we're committing directly to git, wrapping us in another comfy blanket of consistent, tested code. While it's possible to get these up and running without introducing another pair of dependencies in our project, I think the time saved is absolutely worth it. 

If you run `$ npm i -D lint-staged husky` these features are just a quick addition to our `package.json` away. Smoosh the below options somewhere within the file: 

```json
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{ts,js,tsx,jsx}": ["eslint --fix", "jest --coverage --findRelatedTests", "git add"]
  }
 }
```

Now, when we `git commit` our files we'll automatically run our linter and any tests that directly relate to what's in our staging area. 

### Bonus 2: VS Code Settings

I find people who use VS Code generally have excellent taste, but I tend to flick between it and Vim depending on whether I'm settling down for a while or just nipping in to a file to make a couple of tweaks. VS Code is prepared to do a lot of our heavy lifting, but as we've already configured so much elsewhere it's worth letting VS Code know it can ease off a bit.

First, grab the [ES Lint extension for VS Code](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). Then add the below to your `settings.json`:

```json
"eslint.autoFixOnSave": true,
"eslint.validate": [
    {
        "language": "javascript",
        "autoFix": true
    },
    {
        "language": "typescript",
        "autoFix": true
    },
    {
        "language": "javascriptreact",
        "autoFix": true
    },
    {
        "language": "typescriptreact",
        "autoFix": true
    }
],
"eslint.alwaysShowStatus": true,
"editor.formatOnSave": true,
"[typescript], [javascript]": {
    "editor.formatOnSave": false
}
```

This tells both VS Code to take a break and ESLint to stop lounging around and do some work when it comes to JavaScript and TypeScript, and now our linting config should dutifully kick in and autoformat whenever we save. Neat!

***

Has this post been useful for you? I'd really appreciate any comments and feedback on whether there's anything that could be made clearer or explained better. I'd also massively appreciate any corrections!