Published on: 06, by
About a month ago, I was placed in a fairly large project with 40+ microservices, 50+ git repos, managed by three teams of 8 engineers on average.
The project was mainly based on plain JavaScript with minimal documentation. And because JavaScript is dynamically-typed, it is very challenging to read and use any of the custom libraries.
Oh how I long for a strongly typed language!
To be fair, the team had good unit tests which gave me hints on the signature of the functions.
In my spare time, I started researching TypeScript and it's tooling ecosystem. It turns out, in most of the JS tools/libraries, nowadays there is a counterpart (or type-definition) in TypeScript.
Below are the tools and knowledge that I found useful working with TypeScript project.
[TOC]
[[TOC]]
$ npm install -g typescript
Then within a node project, you can initialize as ts project by generating the tsconfig.json
file.
$ tsc --init
Tools can help with the efficient development of a TypeScript project. Here I will cover two categories: testing and guardrail.
There are many testing frameworks including :
I found Jest to be feature rich with a healthy community, so I will focus on it.
Here are my suggestions with a super quick usage guide: The guide assumes you already have node installed.
Use Jest, OOTB it includes coverage (istanbul), spies, and other useful features
$ yarn add --dev jest @types/jest ts-jest
Once installed you can add in package.json
the script "test": "jest"
Configure coverage in Jest. (Jest includes Istanbul)
In jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
setupFiles: ["dotenv/config"],
collectCoverageFrom: [
"**/*.{js,ts}",
"!**/node_modules/**",
"!**/coverage/**",
"!*.config.js",
"!**/env.ts",
],
coverageReporters: [
"json",
"lcov",
"text"
],
transform: {
".ts": "ts-jest"
},
coverageThreshold: {
"global": {
"branches": 2,
"functions": 4,
"lines": 10,
"statements": 10
}
},
};
To run the test with coverage $ jest --coverage
or $ yarn test --coverage
For mocking HTTP requests, use nock, the http server mocking and expectation library. → It will facilitate testing your libraries that depend on external services.
$ yarn add --dev nock @types/nock
For automated browser testing, use Playwright. It uses Chrome protocol, supporting event model → No need to rely on flaky sleep()’s, yielding more reliable tests.
Guardrails prevents you from making mistakes.
static code analysis, use typescript-eslint. A tool based on ESLint (TSLint has been deprecated in favor of typescript-eslint)
$ yarn add --dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
Add .eslintrc.yml
file with
extends:
- eslint:recommended
- plugin:@typescript-eslint/recommended
- prettier/@typescript-eslint
- plugin:prettier/recommended
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: 2017
sourceType: module
plugins:
- "@typescript-eslint/eslint-plugin"
rules:
curly: error
"@typescript-eslint/no-explicit-any": off
"@typescript-eslint/no-var-requires": off
Also you can add files and folders to be ignored in .eslintignore
In package.json
add script to execute with npm
or yarn
:
"lint": "eslint --fix --ext .ts,.json ./src"
For code formatting, use Prettier → Team, and y ourself, will adhere to formatting standards, improving readability and avoiding unnecessary diff lines on MRs.
$ yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
Add .prettierrc.js
file with
module.exports = {
semi: true,
trailingComma: "all",
singleQuote: true,
printWidth: 120,
tabWidth: 4
};
In package.json
add a script: "test": "jest"
To add a git hook configuration that is checked in the repo, use husky → this will share the git hooks with the rest of the team members.
$ yarn add --dev husky
In package.json
add
"husky": {
"hooks": {
"pre-commit": "yarn lint",
"pre-push": "git diff HEAD --quiet && yarn test"
}
}
For faster lintin (linting only those that has changed, uss lint-staged → this will lint only those files that are staged reducing the time it takes to run the lint
$ yarn add --dev lint-staged
In addition, test for vulnerabilities in 3rd party packages using Retirejs.
$ npm install -g retire
$ retire
If you want to reuse a custom TS library (module) in different other projects, you can publish it to a node package registry. Different from a plain ES library, a TS library also requires type definition files to be present for the consuming application to take advantage of the typing information.
This is the quick guide:
Configure tsconfig.json
file to enable declaration → this will generate corresponding .d.ts
files
compilerOptions
, add (or uncomment)"declaration": true
In package.json
add types entry.
"main": "./dist/main.js",
"types": "./dist/main.d.ts"
"typings"
field is synonymous with "types"
Set dist
as distribution folder
tsconfig.js
file to have outDir
set to dist
.dist
to be checked, so add it in .gitignore
..npmignore
based on .gitignore
, but without the dist folder → Without the .npmignore
the publish action will take .gitignore
for the files to ignore publishing.Modify the package.json to include publishing details
...
"publishConfig": {
"registry": "<URL-OF-YOUR-OWN-REGISTRY>"
},
"prepublish": "tsc"
...
publishConfig.registry
is needed if you want to publish in a different registry.Once the above is done, you can publish the package to the registry by running
$ yarn publish
Or alternatively
$ npm publish
Yarn will ask for a new version, whereas npm will not.
The registry will probably fail with 401 if you use the same version to publish, as they are not meant to be overridden.
It is possible to assign scope to packages, an example would be @material-ui/core
. Scoping can eliminate conflict with other module names. Also allows associating different registry locations.
If you are publishing a module, to use scope, the package name should start with the scope name, e.g.
# this is package.json
{
"name": "@my-group/my-amazing-module"
...
}
If you are using a module and want to associate a scope to a specific registry, you need to add the details package manager’s rc files
For NPM
Add in the ~/.npmrc
file (in Windows %USERPROFILE%/.npmrc
):
@<SCOPE>:registry= https://MY-REGISTRY-URL
Or you can also do it by npm
command:
npm config set @<SCOPE>:registry
For Yarn
The file to modify is .yarnrc
"@<SCOPE>:registry" "https://MY-REGISTRY-URL"
You wIll need to enable source map generation.
In tsconfig.json
set either
"sourceMap": true,
or
"inlineSourceMap": true,
Published on: 06, by
Edit on Git