DEV Community

Marceau "XioRcaL" Lacroix for Avalon Lab

Posted on

Setting up a typescript project in 2021

This post will describe how to create a project in typescript from scratch. The final project include some basic code, tests, commit hooks to enforce code formating, automatic tests on push and more ! I hope you'll enjoy it :)

TL; DR

find the code here

Basics

Before you start

  • you should have node/npm (latest lts should do the trick)
  • npm init was run. So you have a package.json

Setting up typescript

Typescript itself

Pretty straight-forward : npm install typescript --save-dev

Now check if that worked ; create a file src/demo.ts and fill it with

export function greeter(person: string): string {
 return "Hello, " + person + "!";
}

console.log(greeter("World"));
Enter fullscreen mode Exit fullscreen mode

And run... Wait... what should I run ? Meh that JS, a node src/demo.ts will do it !
...
Oh no... Why does it complains about some unexpected ':' ?
:-/

Configuring typescript itself

I like to use a recommended default setting for tsc (typescript compiler) : npm install --save-dev @tsconfig/recommended then create a file named tsconfig.json and fill it with :

{
 "extends": "@tsconfig/recommended/tsconfig.json",
 "compilerOptions": {
 "outDir": "./dist",
 },
 "include": ["src/**/*"],
 "exclude": ["node_modules", "dist"]
}
Enter fullscreen mode Exit fullscreen mode

in your package.json add a script for build : "build": "tsc",
you should be able to run npm run build then node dist/demo.js
Nice !

Speeding things up

well I'm the kind of person that does not like to have to type 2 commands everytime my code changes. So lets spend some time automating it a bit

first add tsc-watch to the project: npm install tsc-watch --save-dev
then add a watch script to the package:
"watch": "tsc-watch --onSuccess \"node ./dist/demo.js\"",
now you can run npm watch and every change you made will imediately be complied and run !

Enforce the "2 spaces or GTFO" law !

formatting will be done with eslint and prettier, enforcing formatting will be done with husky and its pre-commit hooks

configuring style tools

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-import prettier
Enter fullscreen mode Exit fullscreen mode

in .eslintrc.js file :

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: 'tsconfig.json',
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint/eslint-plugin'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
  ],
  root: true,
  env: {
    node: true,
  }
};

Enter fullscreen mode Exit fullscreen mode

in .eslintignore :

node_modules
dist
Enter fullscreen mode Exit fullscreen mode

in .prettierrc.js :

module.exports = {
  trailingComma: "all",
  tabWidth: 2,
  singleQuote: true,
};
Enter fullscreen mode Exit fullscreen mode

add a lint script to package.json :
"lint": "eslint src --ext .js,.jsx,.ts,.tsx"

Automate it

Once again, I'm lazy, so I need those tools to run automatically for me. That's where husky commes to play!

npm install --save-dev husky pretty-quick
Enter fullscreen mode Exit fullscreen mode

add a prepare script : "prepare": "husky install"
run it : npm run prepare
you should see a .husky directory now.
create a file nammed pre-commit in this directory and fill it with :

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install pretty-quick --staged
npm run lint
Enter fullscreen mode Exit fullscreen mode

now your formatting will be fixed on commit \o/

enforce the "meaningful commit or GTFO" law !

this will be done with commit-lint
npm install --save-dev @commitlint/config-conventional @commitlint/cli
then create a commit-msg file in .husky directory with :

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit "$1"

Enter fullscreen mode Exit fullscreen mode

and a commitlint.config.js with module.exports = {extends: ['@commitlint/config-conventional']} in it.
Now your commit messages are force to obey the law !

Prevent your friends to break the law

husky take care of that for you :)

These are not the tests you are looking for

Set-up

I use jest so we need to install it first: npm install -D jest ts-jest @types/jest
then config, jest.config.js :

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
Enter fullscreen mode Exit fullscreen mode

add a test script : "test": "jest"

Your very first test

in demo.spec.ts we are going to ensure that our greeter is really polite:

import {greeter} from './demo'

describe('greeter function', () => {
    test('a greet should start with Hello', () => {
      expect(greeter("Bob").startsWith('Hello')).toBe(true)
    });
});
Enter fullscreen mode Exit fullscreen mode

try it with npm run test

Guess what...? AUTOMATION

This time no external deps nor manual tweaking ! jest took care of that for us. simply run jest --watch ! Your tests should run on source edit. Alternatively you can use an extension in your ide to do it.

Setting up your dev env

I'm using vscode so this is the one that will be covered by this section.

Extensions

I suggest enabling code coverage overlay for jest (io.orta.jest.coverage.toggle).
Using prettier as formatter directly in editor will instantanely format your code correctly rather than on commit.

Time for release ?

Ok so your greeter is now ready for its first release ! Lets take advantage of our strict commit message policy to have a nice changelog ! standard-version will help us do so
npm i --save-dev standard-version
and a release script : "release": "standard-version"
finally run npm run release -- --first-release to do your... first release !

automate it, noob !

using github actions because those are pretty neat. create a .github/workflows/ directory to setup those actions.

auto test on commit

This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
For more information
node.js.yml :


name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [12.x, 14.x]
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test
Enter fullscreen mode Exit fullscreen mode

auto publish on release

npmPublish.yml:

name: Publish package on npm

on:
  release:
    types: [created]

jobs:
  publish-npm:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1
        with:
          node-version: 12
          registry-url: https://registry.npmjs.org/
      - name: Cache node modules
        uses: actions/cache@v1
        with:
          path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-
      - run: npm ci
      - run: |
          if [ ${{ github.event.release.action }} = "prereleased" ]; then
            npm publish --tag beta
          else
            npm publish
          fi
        env:
          NODE_AUTH_TOKEN: ${{secrets.npm_token}}
Enter fullscreen mode Exit fullscreen mode

NPM_TOKEN is a repository secret holding an access token with publish rights from npm. get your own at https://www.npmjs.com/settings/{YOUR_USER}/tokens

fun stuff

haha you whish

Top comments (0)