TypeScript Node.js is a match made in web development heaven! Learn how to migrate to TypeScript with this tutorial

By Piotr Baran

You’re using Node.js to create efficient apps? Good for you! But is your development process just as efficient? TypeScript can go a long way to improving your Node.js project! I was able to realize it first-hand during a recent project of mine. I’m going to use it as an example to review the TypeScript Node.js relationship and show you why and how to move from JS to TS in a Node application.

By the time I tell you all about my project, you’ll come to a striking realization:

A lot of its issues could have been avoided simply by switching from JavaScript to TypeScript.

While I’m on it, I’m going to answer a couple of questions:

  • What kind of advantages does TypeScript hold over JavaScript when it comes to Node.js apps?
  • How (and when) to migrate from JavaScript to TypeScript in a Node.js project?
  • How to start a new Node.js project with TypeScript?

Before I go there, it might be encouraging to take a look at the present state of TypeScript in 2022.

TypeScript popularity in 2022

The initial TypeScript release was modest and the new JavaScript superset lacked mature IDE support. When a Microsoft team made it public in 2012 to help sort out a couple of JavaScript shortcomings, the developers could not have suspected that one day it could get this big. 

And yet here we are in 2022 – at a time when TypeScript is a powerhouse in the world of web development. Just how much of a powerhouse, you may ask?

Well, according to our State of Frontend 2022 report, as many as 84,1% of all developers surveyed used TypeScript over the last year.

Percent of developers who answered “yes” when asked about using TypeScript in the last year

These are mostly frontend developers, though. What about a TS Node dev?

Compared to the frontend, TypeScript was somewhat slower to dominate JavaScript on the web server side. Node.js, being the most popular JavaScript runtime environment, eventually made it possible for TypeScript to be used for Node development (as well as some other compile-to-JS languages), but it continues to only support JavaScript fully. What’s more, various tools from the broadly understood Node.js environment now support TypeScript, including the increasingly popular NestJS framework.

Ryan Dahl, one of Node’s original founders, is now promoting Deno – a TypeScript-first Node.js alternative. However, it’s still light years behind Node.js in terms of popularity and community support.

And that’s just too bad because I constantly come across Node.js projects that could use some TypeScript! Why? Let me tell you something about a project I recently completed that really succeeded… in annoying me.

Source: Wikimedia Commons

Ryan Dahl is one of the creators of the Node.js runtime. He continued to work on it until 20212. Eventually, he went on to create a Node.js alternative – the TypeScript-based Deno. He explained his views on the development of Node in the lecture 10 Things I Regret About Node.js.

The legacy Node.js project – woes of JavaScript

From one of our customers, I inherited a legacy application written in Node.js. It had Express.js on the backend and React on the frontend. What’s more, the application had no unit tests whatsoever. You can imagine that learning the codebase was pretty painful without TypeScript.

Every time I wanted to be sure what a given argument is, I had to dig into the code and analyze it thoroughly. And even when I did just that, I still experienced unexpected behavior, especially when the variable in question had a complicated structure full of various dependencies. 

I think that every dev had to face a situation when you have to go six functions backward to analyze how certain parameters would behave in the last function. As I was performing this thankless job, I could not stop thinking how much easier it would have been if it had been written in TypeScript.

In what ways would TypeScript make it better exactly? There are at least four areas I’d like to mention:

  • Predictability,
  • Maintainability,
  • Scalability,
  • Testability.

Let’s take it from the top.

Node.js, TypeScript, React, and Express are all among The Software House’s top technologies. Check out our Tech Radar to find out more about our tech stack.

Predictability

The main issue with JavaScript in this legacy project is that you can’t be sure how a variable will transform during code execution. Here, the variables included some complex objects with multiple nested fields. Sometimes, an object key would get lost somewhere by the time another method that needs it receives the variable as an argument. And then boom – a bug strikes with obj.key is undefined. TypeScript’s static types make it easy to detect such bugs early on.

Another time, seemingly out of nowhere, a field that has always been an array magically transforms into an integer.

Sure, there are some solutions to that. But it gets really time-consuming when you have to run a check with hasOwnProperty or wonder what’s the best way to find out if a variable is an array every time you want to use it.

Maintainability

TypeScript ensures that you know what to expect from variables you work with. Admittedly, setting up the interface for objects takes a lot of time in the beginning,  but it saves much more during debugging. 

When you have to delete an object field, you need to use a destructor. That way, TypeScript forces you to think about what you’re doing. As a result, TypeScript does its best to prevent you from shooting yourself in the knee every time you change your code.

Scalability

The unpredictability of JavaScript makes it harder to add new features to the project. You might feel uncertain about what is returned from the method you used. When you use TypeScript, you don’t need to remember all that information and you can focus simply on writing new code.

Testability

Testing is also affected by the lack of static typing in JavaScript. TypeScript requires less testing code than JavaScript to achieve the same amount of certainty about the application. Even writing tests is easier since TypeScript helps you set up correct mocks by implementing defined interfaces.

TypeScript continues to get better and better! Don’t believe it? Check out the new TypeScript features article to get the latest information.

When to move to TypeScript

You may be wondering when is the best moment to move from JavaScript to TypeScript code. The truth is that when it comes to existing apps, the process will inevitably be difficult and time-consuming. Most likely, you will hit the wall and wonder why you even started doing this TypeScript project in the first place. 

Having said that, the sooner you turn to the TypeScript compiler the better. Bigger apps will surely take a lot more time to migrate to TypeScript so you should make your decision quickly, early in the lifespan of your Node.js application. Maintaining a growing Node.js app without static types from TypeScript is going to get increasingly hard. It is just impossible to remember what every variable does in the code. 

When to migrate your Node.js app to TypeScript ASAP?

  • When your application handles data of great value and importance, especially money.
  • When the application moves from the proof-of-concept state into the real world.
  • When you know that the application is going to get support for several years going forward.
  • When you are going to keep adding new features to your application.
  • When the application codebase is still relatively small.

There is one more reason of a different kind that I’d like to point out. It’s something called “trusting your gut”. For example, when adding a new feature in JavaScript to your Node.js project, you may feel uneasy, like it is a matter of time before it all explodes. 

The positive side of the migration process is that you will learn a lot more about your code and probably encounter a lot of refactoring opportunities.

The highly scalable ticket platform of Reservix was developed with Node.js and TypeScript. Read the full case study.

The implementation – moving an existing Node.js app from JavaScript to TypeScript

Before you can start moving the codebase of your Node.js application to TypeScript, you need to add some parameters to your TypeScript configuration file tsconfig. In particular:

  • allowJs so that you can import .js files into .ts files,
  • checkJs so that you can report errors in JavaScript files. 

Now, try to get an error-free compilation while using any types. Then, solve error issues incrementally and replace any type with interfaces. In the beginning, you don’t need to have types for everything. You can add types to core functionalities of your Node.js application step by step. Remember to install types for packages.

From JavaScript to TypeScript

I want to give you an example of how I would approach moving a piece of JavaScript code in a Node.js project to TypeScript. First, you need to set your first TypeScript file (a tsconfig) file with allowJs and checkJs. You also need to set a strict type check but you can adjust type checks to your needs as described here.

Here is an example JavaScript file from which I’m going to extract functionalities to TypeScript classes.

The file sends an invitation email and a request email depending on the emailType parameter obtained from the request. Out of the file above, I created multiple TypeScript files using OOP (Object-oriented programming) rules such as the single responsibility rule and encapsulation principle.

Now, I need to create a generic EmailService class.

The EmailServiceConfig interface I imported defines the structure of dependencies that this class takes upon constructing. In a situation like this, it’s a good idea to use some kind of dependency injection container such as Awilix. It helps manage objects with a lot of dependencies. I skipped it here for simplicity’s sake.

I also created a nodemailer transporter in the constructor. Then, I made an interface to define the sendMail method parameters. You can see that the EmailService class is more self-described and error-proof and that it is only responsible for sending emails. To that end, it needs to create nodemailer. It also uses the following config file:

When you create an EmailService object, you need to pass it to it as a dependency like this: 

const emailService = new EmailService(emailServiceConfigFactory(process.env)); 

That way, you have validated your config and you know for sure that you have everything necessary to create a transporter for nodemailer

Splitting responsibilities between classes

Now, I’m going to show you a more specific class. It’s responsible for sending invitation emails.

Apart from sending an invitation email, this class creates a JSON Web Token (JWT) in order to make a magic link. As a dependency, it takes the aforementioned EmailService. It also uses the InvitationTemplate, which is simply a collection of exports:

How can you use these classes? Consider the following example:

It would have looked prettier with a dependency injection container, but TypeScript will remind you about necessary dependencies anyhow. With those classes, you split a lot of responsibilities between them. Now, whenever you get into a class, you are able to understand what is going on much more quickly. The classes are easier to test as well.

Moving an existing app to TypeScript is difficult.

Hire devs that have already done this time and time again.

Building a TypeScript app in Node.js from scratch

As you can see, transitioning your current code base to TypeScript is challenging. Oftentimes, you will have to run with TypeScript and JavaScript code at the same time and solve their problems simultaneously. It’s way easier to start a new project in Node.js with TypeScript.

If you want to start your TypeScript Node.js project from scratch, you need to install Node.js, and then initialize the project with npm init. Then, you go through the wizard and fill in the necessary data. 

Once the initialization is complete, install TypeScript with the npm install typescript –save-dev command. Add types for Node with the npm install @types/node –save-dev command. 

Now you can create an index.ts file that has your starting “Hello World!”. Then, run the npx nodemon ./index.ts command

To take a step further, set up your tsconfig.json. It describes the way the compiler treats your TypeScript files when compiling them to JavaScript. For this project, I’m going with something like this:

It’s a good moment to create some environment variables. Since every project has a ‘.env’ file, create one and set some variables such as STAGE to indicate if you are in the production or development environment. Then, set APP_NAME and PORT. 

At this point, the .env file should look like this:

The next step would be to add some validations for these environment variables. First, you need to install the required packages using the npm i celebrate ts-pipe-compose command. That would make the appConfig.ts file look like this:

What happened here? Firstly, I installed celebrate, which is middleware for joi. The celebrate package will be also useful later for validating parameters from HTTP requests to your Node.js application. I also installed the ts-pipe-compose package. It makes it possible for me to perform pipeline(loadConfig, validateConfig). With that, whenever I call appConfigFactory(args), it results in validateConfig(loadConfig(args)). To put it in different words, it first loads the config and then validates it against your defined joi schema. 

You can now use appConfigFactory in your index.ts file, in which I will also define the routes of the application. For that, I need to install express and dotenv packages first. The relevant commands are npm i express dotenv and npm i –save-dev @types/express respectively.

You can now create your app.ts file, which has the class that initializes your Node.js app.

Create the index.ts import app in it and run the init method:

To sum it all up, the structure of directories in your project should now look as follows:

|-node_modules

|-src/

|–config/

|—app.ts

|–app.ts

|–index.ts

|-.env

|-tsconfig.json

In order to run the project, just use the npx nodemon ./src/index.ts command. When you send the get request with the arg param and a string value to localhost:3337, you should get the following response: “Nodejs, TypeScript, Express App”. You should also get the string value of your param. Otherwise, you will get an error that says: “Validation failed”. 

When everything is alright, you can proceed to extend the project, creating some models, and controllers with adequate routes for each.

Last but not least, I want to mention the project directory structure. Out there in the wild, there are so many projects with empty directories, created out of habit and for no good reason. Do not force yourself to fit a new class into an existing directory in your Node.js project. Create a new one only if it is really necessary. 

Express-boilerplate by The Software House

To help you start with your new TypeScript Node.js application, TheSoftwareHouse has created an express boilerplate. It helps a ton in setting up new projects. The whole boilerplate is based on docker containers so that you can start developing in no time. Apart from that, it also has plenty of useful tools. Be sure to check this out.

Check out the express-boilerplate by The Software House

Node.js TypeScript combo – trends & predictions

TypeScript is getting more and more attention and rightfully so. 

After all, it helps you keep big projects at bay. It makes it easier to create good clean code. As a result, more and more new and existing tools choose to support TypeScript or even use it as its foundation.

The JavaScript development team is constantly trying to catch up with TypeScript by adding plenty of new features, once only available in TypeScript. Nowadays, vanilla JavaScript has support for decorators, and types for IDE. The ES2022 specification went as far as introducing private variables. 

But whether NodeJs is ever going to be TypeScript-first is highly doubtful due to the huge amount of baggage that Node.js has generated over the years.

Deno and Bun both start with clean sheets and offer native support for TypeScript. However, developers are still hesitant to use either of them in a production environment. It seems that Deno is much closer to being ready for some production projects.

It remains to be seen whether Deno will emerge as a serious alternative to Node.js
Source: https://deno.land/

The best devs keep tabs on the latest innovations in their fields.

As part of our internal workshops, our Node.js and frontend developers learn all about the latest techniques and approaches. Perhaps they can use their knowledge to your advantage?

Conclusions & lessons learned

As you can see, TypeScript really does make a difference, especially in a big project.

  • Due to its benefits for both simplicity and maintainability, TypeScript is a huge help for both developers who are only starting with a project and those who have been working on it for years.
  • TypeScript takes away a lot of the project overhead, giving developers more spare time and brain capacity to focus on different aspects of coding than maintenance and testing, especially the development of new business logic.
  • Moving to TypeScript is harder with an existing JavaScript codebase, but it is not mission impossible.
  • In order to make up your mind about migrating to TypeScript, you need to answer questions such as: How long will the project be maintained? How big is it? How many of our developers already know TypeScript?

When the answers indicate that implementing TypeScript will be relatively easy and provide its benefits in the long run, you should definitely go for it!

Just released!
The State of Frontend 2024

Performance is the #1 challenge in 2024. 6028+ answers analyzed.

Read now

The Software House is promoting EU projects and driving innovation with the support of EU funds

What would you like to do?

    Your personal data will be processed in order to handle your question, and their administrator will be The Software House sp. z o.o. with its registered office in Gliwice. Other information regarding the processing of personal data, including information on your rights, can be found in our Privacy Policy.

    This site is protected by reCAPTCHA and the Google
    Privacy Policy and Terms of Service apply.

    We regard the TSH team as co-founders in our business. The entire team from The Software House has invested an incredible amount of time to truly understand our business, our users and their needs.

    Eyass Shakrah

    Co-Founder of Pet Media Group

    Thanks

    Thank you for your inquiry!

    We'll be back to you shortly to discuss your needs in more detail.