Node ❤ TypeScript

Whenever you have the possibility to develop your Node.js application with TypeScript you should do so. It provides you some clear advantages and it comes with nearly no overhead. You will see how easy it is to integrate TypeScript in your project and even use popular libraries and frameworks such as Express.

TL;DR

  • npm init to create a package.json file
  • Install typescript and nodemon (locally)
  • Create a tsconfig.json
  • Create npm scripts for the most common tasks
  • Install all the packages you need
  • Install type definitions for your packages
  • Run the TypeScript compiler in watch mode combined with nodemon
  • Compile your application once before going live and run it with node

First of all: Why TypeScript?

As I just mentioned, TypeScript has some advantages over plain JavaScript:

  • Types: Besides the predefined types of TypeScript itself every class and interface defines a new type in your application. You use these types to indicate the type of objects in your application. TypeScript uses a technique called duck typing for type checking. Alex Martelli introduced this term in a usegroup: In other words, don’t check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc., etc., depending on exactly what subset of duck-like behaviour you need to play your language-games with. For our TypeScript project this means: If an object has all the methods and properties of a certain type, it is considered as an instance of this type.
  • Signatures: With the type system you can define the types of function parameters and return values. The compiler checks the parameter types on every function call and throws an error if you violate the rules. The same is true if you try to return a wrong type in your function. If you don’t provide a type for a variable, parameter or return value, TypeScript tries to guess the correct type of this variable. This process is called type inference. If it doesn’t find an appropriate type the compiler emits an error.
  • Module system: Like modern JavaScript, TypeScript also supports modules. With this feature you can build your application as a collection of separate files that import classes, objects and functions from other files and export structures on their own. The good news is: TypeScript uses the JavaScript module syntax. As long as Node doesn’t support the import and export keywords on it’s own you can already use them in TypeScript context. They will be transformed to require and module.exports statements by the compiler.
  • Static code analysis: The center piece of TypeScript is the compiler which transforms your TypeScript source code into JavaScript. While processing your source code it throws errors if you violate the rules of the language. In most of the cases the erroneous compilation result is even executable. But you should always try to follow a zero error policy.

What do you need?

Before you start working on your application you first have to initialise your application.

npm init

With this command you create a package.json file that holds all important meta information about your application such as the packages you’re using.

In order to use TypeScript in your application you have to install some packages. First of all you need the typescript package that contains the compiler.

npm install -D typescript

It’s up to you if you install typescript globally on your system or locally in your project as a devDependency. I suggest the latter one as it enables you to use different TypeScript versions in your project on one system.

While you’re developing your application you can run the TypeScript compiler in watch mode to automatically recompile the source as soon as you save your changes. Your Node process won’t get notified of these changes. In order to accomplish this, you’ve got to install another tool that restarts the process for you. There are numerous of these tools. One of the most popular ones is nodemon.

npm install -D nodemon

You should install this package also as a devDependency as you only need it while developing your application.

With this setup you can start working on your application.

If you’re planning to use stuff that is not written in TypeScript (such as Node itself) you should install the type definitions for it. Type definitions define the interfaces for all available classes and functions of a package such as Express provides. There is a huge collection of type definitions in a project called Definitely Typed (definitelytyped.org). All the type definitions are prefixed with @types followed by the name of the package. e.g. @types/node or @types/express. You install it with the following command:

npm install -D @types/node

The TypeScript compiler automatically loads all definition files located in the node_modules/@types folder. So you don’t have to take care of it.

A very simple example

TypeScript is able to generate a basic configuration for you. With the following command you generate a tsconfig.json for your project.

node_modules/.bin/tsc --init

This file contains the target version your JavaScript code is generated in, which should be es2016. The module property specifies the generated module format. In our case this has to be commonjs. Another important directive is outDir which is where the generated JavaScript files are saved. Usually you enter a foldername like dist here. After you’re done with configuring, you can finally write your TypeScript code.

Basically a piece of valid JavaScript code is also valid TypeScript code. For our first test it’s enough to just log a string to the console and save it to a file with the name index.ts.

console.log('Hello World');

index.ts

In order to execute it, you first have to compile it with

node_modules/.bin/tsc

This command generates an index.js file in the dist folder which is also created for you if it doesn’t already exist. To run it, you just have to enter

node dist/index.js

Shortcuts make your life easier

This node_modules/.bin thing is a little bit annoying, you have to admit. But there’s a simple solution: In your package.json file there is a scripts section. Here you can create a lot of shortcuts which are basically just shell commands that are executed by npm. Here is a block of some handy commands:

"scripts": {
  "dev": "npm run tsc & npm run nodemon",
  "tsc": "tsc -w",
  "nodemon": "nodemon",
  "build": "tsc",
  "start": "node dist/index.js"
}

package.json

There are some scripts, such as start, which could be executed directly with npm via

npm start

and others which are executed with npm run such as dev

npm run dev

You can find a detailed explanation on script here: https://docs.npmjs.com/misc/scripts

As soon as you’ve got a console output, you can dive a little deeper.

Using the module system

Usually you have classes in your application representing the structure of your data objects and collections of methods which contain your business logic. To keep your application maintainable as it grows, you should follow the pattern of loose coupling and tight cohesion. That means create modules and put everything of a certain topic into a file of its own and communicate via defined interfaces. A module exports a structure you can import elsewhere and use it. To see it working, let’s create a representation of a person and use it in our index file.

export class Person {
  constructor(
    private firstname: string,
    private lastname: string
  ) {}

  getFullName() {
    return`${this.firstname}${this.lastname}`;
  }
}

person.ts

import { Person } from './person';

const basti = new Person('Basti', 'Springer');

console.log('Hello ' + basti.getFullName());

index.ts

As you can see, import and export are working just as expected. To test your code, just run

npm run dev

Bringing it together: An Express application

So far it wasn’t a big deal using TypeScript and Node together in one application. Integrating other libraries and frameworks in your application is pretty easy as well. In the next step we will use Express to create a web application. You simply install the framework and its type definitions and start implementing.

import { Person } from './person';
import * as express from 'express';

const app: express.Application = express();

app.get('/', (req: express.Request, res: express.Response) => {
  const basti = new Person('Basti', 'Springer');
  res.send('Hello ' + basti.getFullName());
});

app.listen(8080, () => console.log('listening to http://localhost:8080'));

index.ts

The only notable sections in the source code are the import of express and the type hints for the app object and within the signature of the routing callback function. If you have your dev task running, your node process will be restarted and you can open your browser and check the result.

Leave a Reply

Your email address will not be published. Required fields are marked *