How to setup TypeScript for NodeJS project
Tags: typescript, node, javascript
Published: 2020-07-16 07:16:32

*This post was originated on my personal site: [https://peterhub.dev](https://peterhub.dev/posts/how-to-setup-a-nodejs-project-with-typescript)*

In this short tutorial, we'll go through the process of creating a basic **Node+TypeScript** application. It's really easy!

### **Prerequisites**
- You should know about Node + npm and have them installed on your machine

- You have your favorite IDE ready

### **TypeScript**
> TypeScript is a language for application-scale JavaScript. TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript.

This means TypeScript is much better suited for creating long-lasting software and having the compiler help catch bugs and validate types is extremely helpful.

***

### **Setup**
``` bash
mkdir typescript-node-example
cd typescript-node-example
npm init -y
npm install --save-dev typescript @types/node ts-node nodemon tsconfig-paths
```

The first three lines of command are easy to understand, let's see what we've got from line **#4**.


1. **typescript**: Of course we need to install TypeScript. After installing, we have access to the command line TypeScript compiler through the `tsc` command

2. **@types/node**: This package contains type definitions for Node.js. It provides type safety and auto-completion on the Node apis like `file`, `path`, `process`, etc.

3. **ts-node**: It allows the app for running TypeScript code directly without being compiled.

4. **nodemon**: This will watch for code changes and restart automatically when a file is changed.

5. **tsconfig-paths**: We use this to load modules whose location is specified in the `paths` section of `tsconfig.json`. More detail [here](https://github.com/dividab/tsconfig-paths#readme)

### **TypeScript Configuration**

We start with `npx tsc --init`, which will generate `tsconfig.json` file. The default configuration after removing all commented code:

``` json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
}
```

It won't work yet. We still need to define some properties like **outDir**, **rootDir**, **baseUrl** and **paths**

- **outDir**: Where TypeScript generates our compiled code. We specify it to be in `build/` folder.

- **rootDir**: This is where TypeScript looks for our code, and we'll write our TypeScript there.

- **baseUrl**: Base directory to resolve non-absolute module names. As we don't want relative import path hell like this `import {functionA} from "../../../libs/fileA"`

- **paths**: A series of entries which re-map imports to lookup locations relative to the baseUrl we define above

After that, we have the final version of `tsconfig.json`

``` json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "build",
    "rootDir": "./",
    "baseUrl": "./",
    "paths": {
      "*": ["*", "build/*"]
    }
  }
}
```

### **Setup code reloading script**

Create file `nodemon.json` at the root folder (same level as `package.json` file)

``` json
{
  "watch": ["./"],
  "ext": ".ts,.js",
  "ignore": [],
  "exec": "ts-node -r tsconfig-paths/register ./index.ts"
}
```

Notice `-r tsconfig-paths/register` part? We'll discuss it later in the **Development** section

### **Development**

Let's create `index.ts` and `libs/number.ts`.

``` typescript
// index.ts
import { randomNumber } from "libs/number";

console.log("Random number (1 - 100): ", randomNumber());
```

***

``` typescript
// libs/number.ts
export function randomNumber(): number {
  // random from 1 to 100
  return Math.floor(Math.random() * 99) + 1;
}
```

The code is pretty straight forward. For the sake of this short tutorial, let's keep it simple like that 😄. In `package.json`, add `"dev": "nodemon"` inside scripts. Now we start the application in development with

> npm run dev

Earlier I mentioned about `-r tsconfig-paths/register` part, it means to help `ts-node` and `node` to understand absolute path like `import ... from "libs/number"`

### **Build**

To compile ts files into js, add this line inside scripts in package.json

> "build": "rm -rf ./build && npx tsc"

This script will remove old `build` before the TypeScript compiler injects new code into it.

### **Production startup**

For executing the app in production environment, we have to run `build` command first, and then run the compiled JavaScript at `build/index.js`. Those two tasks can be grouped in a script below:

> "start": "npm run build && node -r tsconfig-paths/register build/index.js"

After adding all the necessary scripts, this is the entire `package.json` file

``` json
{
  "name": "typescript-node-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon",
    "build": "rm -rf ./build && npx tsc",
    "start": "npm run build && node -r tsconfig-paths/register build/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^14.0.22",
    "nodemon": "^2.0.4",
    "ts-node": "^8.10.2",
    "tsconfig-paths": "^3.9.0",
    "typescript": "^3.9.6"
  }
}
```
Yeah that was it, simpler than you could imagine 😁