avatar

Andres Jaimes

Setting up node and typescript

By Andres Jaimes

- 5 minutes read - 927 words

This article describes the creation process of a node application with TypeScript.

Installing nvm

There are multiple ways to install node but we have picked the nvm path. This method will allow us to have multiple versions of node installed on the computer.

We have to make sure that xcode command line tools are installed. We can trigger the installation by trying a command like cc on the terminal.

Run the following command to install nvm:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

The previous command will automatically install all the files to ~/.nvm and update the shell initialization file. To reload the shell configuration run:

source ~/.zshrc

We can now try nvm:

nvm --version

Installing node and npm

The next step is to install node and npm (node package manager) by using nvm.

nvm install node

wait for the proces to complete and try the following commands:

node --version
npm --version

The previous commands have installed the latest versions of node and npm. To install and use a specific version of node and npm try:

nvm install v12.18.0
nvm use v12.18.0

To list all the versions available on our computer:

nvm list

To get the installation directory for a specific node version:

nvm which 18.10.0

Creating a new project

Start by creating a new directory for our project, cd into it and initialize the project with the following command:

npm init

The previous command will generate a file called package.json.

Adding typescript support

Now, enter the following command to add TypeScript support:

npm install typescript --save-dev

Create a file named tsconfig.json along with the package.json file, where we will put our TypeScript configuration:

{
  "compilerOptions": {
    "allowJs": true,            
    "checkJs": true,
    "outDir": "dist",
    "sourceMap": true,
    "target": "ES6",
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "strict": true
  }
}
  • allowJS and checkJs tell TypeScript how to handle native JavaScript code it encounters in our source files.
  • outDir tells TypeScript where to put the converted files,
  • sourceMap instructs it to link the converted code back to the original (for debugging),
  • target tells it to convert our code to a browser-friendly, yet still modern, version of JavaScript.
  • noImplicitAny, noImplicitReturns, and strict impose a series of additional tests on code, which promote best coding practices.

Update package.json:

{ 
  //... 
  "main": "src/index.ts",
  "scripts": {
    // ...
    "build": "tsc && npm run build:cp-public",
    "build:cp-public": "cp ./public/** ./dist",
    "type": "tsc --noEmit"
  }
  // ...
}
  • update main to use src/index.ts instead of the default index.js,
  • build runs two commands, tsc (the TypeScript compiler), and build:cp-public,
  • build:cp-public copies the contents of the public directory to the dist directory,
  • type is triggered by npm run type to help during debugging.

Creating source files and compiling

Create a simple src/index.ts file:

function helloWorld() {
  const element = document.getElementById("hello-world");
  if (element) {
    element.textContent = "Hello, World!";
  }
}

helloWorld();

and a public/index.html file:

<html>
<body>
  <p id="hello-world"></p>
  <script src="index.js"></script>
</body>
</html>

Notice that we are referencing index.js from the html file. That is what the TypeScript compiler will output from our src/index.ts file.

And we are ready to compile the project:

npm run build

The resulting files can be found in the dist directory.

A working example can be found on my github page.

Installing additional modules

First, add the type argument to the script tag to avoid issues with Safari.

<script type="module" src="index.js"></script>

Safari will complain with the following error if the type argument is missing:

SyntaxError: Unexpected token ‘{’. import call expects exactly one argument.

Let’s install charts.js and configure it to work with TypeScript. Start by installing the module:

npm install chart.js

Now try importing the module from index.ts:

import { Chart } from 'chart.js';

const ctx = document.getElementById('csd1') as HTMLCanvasElement;
const myChart = new Chart(ctx, {
  // ...
});

and compile.

npm run build

If the compiler fails with the following error:

Cannot find module ‘chart.js’. Did you mean to set the ‘moduleResolution’ option to ‘node’, or to add aliases to the ‘paths’ option?

we can fix it by setting moduleResolution to node in tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "node"
  }
}

Common questions

Why we have to commit package-lock.json?

guarantee exact same version of every package between your dev and prod environments. This part is the most important when building in different environments at different times. You may use ^1.2.3 in your package.json, but how can you ensure each time npm install will pick up the same version in your dev machine and in the build server, especially those indirect dependency packages? Well, package-lock.json will ensure that. (With the help of npm ci which installs packages based on lock file)

Common issues

Reinstalling all node modules

We want to reinstall all the existing node modules when dependency errors show up. To do it, remove the node-modules directory and the package-lock.json file, and run npm with the install parameter again.

rm -Rf node-modules
rm package-lock.json
npm install

Dealing with CORS issues

Browsers block certain types when loading resources using the file:// protocol. To overcome the issue, we need to set up a web server. An easy way to do it is to use python. The process is very simple, cd into the directory that contains the web resources and enter the following command:

cd <any-directory>
python -m SimpleHTTPServer 9000

or for python3:

cd <any-directory>
python3 -m http.server 9000

if we omit the port, python will use the default 8000 one.

References