// src/app.jsconstexpress=require("express");constapp=express();constport=3000;app.get("/",(req,res)=>{res.send("Hello World!");});app.listen(port,()=>{console.log(`π Server listening at http://localhost:${port}`);});
Run the app:
> npm start
Ensure the app is running by visiting http://localhost:3000 in your browser. You should see "Hello World!".
Setup Typescript
# install typescript and types for node and express> npm i -D typescript @types/node @types/express
# initialize typescript# the following command creates a tsconfig.json file> npx tsc --init
Edit tsconfig.json:
// tsconfig.json
{
// set out dir to dist"outDir": "./dist"
}
Edit package.json:
// package.json
{
"scripts": {
"start": "node dist/app.js", // run the main javascript file from the dist folder"build": "tsc"// compile typescript into javascript
}
}
Update src/app.js to src/app.ts:
// src/app.tsimportexpress,{Request,Response}from"express";constapp=express();constport=3000;app.get("/",(req: Request,res: Response)=>{res.send("Hello World!");});app.listen(port,()=>{console.log(`π Server listening at http://localhost:${port}`);});
Run the app:
# compile typescript into javascript> npm run build
# run the app> npm start
Ensure the app is running by visiting http://localhost:3000 in your browser. You should see "Hello World!".
Setup ts-node-dev
> npm i -D ts-node-dev
{
"scripts": {
// compile typescript to javascript and run the app in watch mode// note: this DOES NOT output the transpiled .js files (ie. no .js files are created)"dev": "ts-node-dev src/app.ts"
}
}
Run the app:
> npm run dev
Ensure the app is running by visiting http://localhost:3000 in your browser. You should see "Hello World!".
Make a change to src/app.ts and save the file. The app should automatically restart and you should see the change in the browser.
Setup ESLint
# install eslint and typescript plugins# go through the options# make sure you choose "json" or "yaml" for the config file format so that you do not have to lint the .js config file> npm init @eslint/config
This plugin gives more control over how imports are sorted than the native ESLint plugin
# install the ESLint plugin to sort imports> npm i -D eslint-plugin-import
Edit .eslintrc.json
{
"plugins": [
// ..."import"// add the "eslint-plugin-import" plugin
],
"extends": [
// ..."plugin:import/recommended", // for the recommended rule set"plugin:import/typescript"// for the typescript rule set
],
"rules": {
// ...// turn on errors for missing imports"import/no-unresolved": "error",
// optionally: turn off errors for named exports that are not exported// (if you get a lot of errors for this, you can turn it off)"import/no-named-as-default-member": "off",
"import/order": [
"error",
{
"groups": [
"builtin", // Built-in imports (come from NodeJS native) go first"external", // <- External imports"internal", // <- Absolute imports
["sibling", "parent"], // <- Relative imports, the sibling and parent types they can be mingled together"index", // <- index imports"unknown"// <- unknown
],
"newlines-between": "always",
"alphabetize": {
/* sort in ascending order. Options: ["ignore", "asc", "desc"] */"order": "asc",
/* ignore case. Options: [true, false] */"caseInsensitive": true
}
}
]
}
}
NOTE: make sure source.organizeImports is set to false in your vscode settings.
source.organizeImports will use VSCode's rule set to organize imports. This may conflict with the ESLint plugin import, and you may get linting errors even after you have fixed the imports.
# install and setup husky> npx husky-init && npm install
Edit .husky/pre-commit:
# keep the top of the file as is# delete: "npm test"# add the following line# runs the "pre-commit" script in package.json when you commit
npm run pre-commit
Edit package.json:
{
"scripts": {
"pre-commit": "npm run lint:fix && npm run format"
}
}
Run the following command to add a pre-push hook
# runs the "pre-push" script in package.json when you push> npx husky add .husky/pre-push "npm run pre-push"
lint staged allows you to lint and format only the files that you have staged
# install lint-staged> npm i -D lint-staged
Edit package.json:
{
"scripts": {
"pre-commit": "lint-staged"
},
"lint-staged": {
// lint and fix only the files that you have staged"*": ["prettier --write --ignore-unknown"],
"*.{js,ts}": ["eslint --fix"]
}
}
Setup Testing with Mocha
# install mocha and its types> npm i -D mocha @types/mocha
# install ts-node to run typescript files with mocha> npm i -D ts-node
Edit package.json to add the test scripts:
{
"scripts": {
// build the app (compile typescript to javascript files), and run all the test files using mocha"test": "npm run build && mocha dist/tests/**/*.js",
// run all the test files in watch mode"test:dev": "mocha -r ts-node/register 'tests/**/*.ts' --watch --watch-files 'src/**/*,tests/**/*'"// -r ts-node/register 'tests/**/*.ts': register ts-node to run typescript files// --watch: watch for changes// --watch-files 'src/**/*.ts,tests/**/*.ts': watch for changes in these files
}
}
Create a test file tests/app.test.ts (create the tests folder beside the src folder, not inside it):
// tests/app.test.tsimportassertfrom"assert";describe("Array",function(){describe("#indexOf()",function(){it("should return -1 when the value is not present",function(){assert.equal([1,2,3].indexOf(4),-1);});});});
Since we now have a tests folder beside the src folder, the dist/ folder will have a src/ and tests/ folder when typescript is compiled to javascript
Update the "start" script in package.json to run the app.js file which is under the dist/src folder:
{
"scripts": {
// run the app.js file which is now under the dist/src folder instead of the dist folder// this is because we now have a tests folder beside the src folder"start": "node dist/src/app.js"
}
}
Run the tests:
# run all the test files using mocha> npm test
Run the tests in watch mode:
# run all the test files in watch mode> npm run test:dev
Setup testing libraries: chai, sinon, and proxyquire
# install chai and its types> npm i -D chai @types/chai
# install sinon and its types> npm i -D sinon @types/sinon
# install proxyquire and its types> npm i -D proxyquire @types/proxyquire
Create a test file tests/app.test.ts (create the tests folder beside the src folder, not inside it):
// tests/app.test.tsimportassertfrom"assert";importchaifrom"chai";importsinonfrom"sinon";importproxyquirefrom"proxyquire";constexpect=chai.expect;describe("Array",function(){describe("#indexOf()",function(){it("should return -1 when the value is not present",function(){assert.equal([1,2,3].indexOf(4),-1);});});});