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 apackage.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"));
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"]
}
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
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,
}
};
in .eslintignore
:
node_modules
dist
in .prettierrc.js
:
module.exports = {
trailingComma: "all",
tabWidth: 2,
singleQuote: true,
};
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
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
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"
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',
};
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)
});
});
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
- Jest (orta.vscode-jest)
- ESLint (dbaeumer.vscode-eslint)
- Prettier - Code formatter (esbenp.prettier-vscode)
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
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}}
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)