For one of the projects we are working on, we are using NodeJS as our backend platform. We started with JavaScript, which can be run natively by NodeJS. Later, we started a sub-project, where we made a decision to try out TypeScript, a statically typed superset of JavaScript, as we’ve seen quite a few benefits that we could take advantage of.
With TypeScript being statically typed, we set a few goals the switch should provide. One of the goals was having a more strict codebase, which should make it easier to review the code authored by other people, easier navigation through the codebase and elimination of bugs which could be prevented ahead of runtime by means of TypeScript’s static analysis. With the goals being set, let’s analyse the results we’ve observed.
In order to ease the setup of the project's repository, we suggest using one of NodeJS TypeScript starter repositories authored by the community. Our starter repository of choice was Microsoft’s TypeScript Node Starter, freely available GitHub.
This specific repository comes with a variety of packages, preset scripts and tooling already set up. It also removes the need to wire everything manually and showcases the project’s folder structure.
It is a widely known fact in computer science that statically typed languages remove a whole class of bugs which would otherwise only be discovered by testing the application. Among them is discovering type mismatches. For example, summing up a number and a string is an undefined operation, which would result in an undefined or weird behaviour of application, but is prevented by a compiler pretty much immediately.
As TypeScript is statically typed language, most of that holds true for it as well. However, being a superset of JavaScript (which means every JavaScript program is a valid TypeScript program), it allows for so-called gradual typing, allowing developers to omit type definitions, which simply means the compiler cannot do type checks. Compared to other well-known statically typed languages, such as for example Java, C and Rust, which force developers to always specify variable types, we believe TypeScript can only do the job well if best practices, suggested by TypeScript’s documentation, are strictly enforced within the codebase.
Static analysis brings us another benefit, and that’s auto-completion. While JavaScript editors do offer it to some extent, we have observed that with TypeScript, it becomes much more useful, which is simply due to the fact that tooling has a much simpler job knowing what function and member properties are available in advance. This not only makes it easier to write new code, it also makes code easier to read and follow. It eliminates the process of guessing what type some specific variable is and what can actually be done with it. Consequently, code reviews are easier and much more efficient.
TypeScript and JavaScript are interoperable, which means one can call JavaScript code from TypeScript. A lot of community libraries are still written in JavaScript, however by using those, we lose the benefits of TypeScript. In order to circumvent this limitation, most JavaScript packages, at least the popular ones, offer its associated type package. These packages have names in the form of @types/package_name. However, one can still find a lot of libraries which do not provide their associated type packages. In such a case, we’re left with two options.
The first one is to write the type package by ourselves. This approach can be tedious and often painful, as in most cases, the library documentation does not state the possible parameter and return types. This means we’ll need to look at the library’s source code and figure out the full type definition by ourselves. If the library has a large number of functions, this process can take days.
The second option is to simply define a module of type Any. Any type is a special type defined by TypeScript, which tells the compiler that a variable's value can be anything. This means that the compiler will not complain about it, but we’re losing aforementioned benefits, including static-analysis and ability for editors to auto-complete our code.
Another disadvantage we’ve observed is the time spent by the compiler to do its job. For local development, compiling after each change is very inefficient, as it can take very long. We’ve eased this issue by using a so-called watcher, which detects which files have been changed, and does only incremental compilation, shortening the overall compile time. Deploying to the server, however, still requires us to compile the whole codebase, which increases deployment times compared to JavaScript-only codebase.
If you’ve got this far, you probably already know that defining types in TypeScript is of great importance. So is type-casting, which is a process of converting one type to another. Perhaps this becomes most noticeable when we’re dealing with calls to external APIs. Of course we have an option not to define the types and not be bothered by the compiler, but doing that goes against TypeScript’s best practices.
In this particular case, we’ve opted to use a validation library. A good TypeScript validation library will solve two issues. Firstly, we can set up validators for external APIs, and secondly, it will generate us the necessary types, removing the need to define them manually.
Most importantly, do not turn off compiler errors and warnings. TypeScript does provide a way to do this, either for the project as a whole, or just for specific lines of code. In our experience, one should do that in very rare cases. Before doing it, stop and review what the compiler is telling you. In almost all cases, we figured out suppressing compiler errors and warnings would actually be a bad idea.
Follow guidelines and TypeScript’s best practices. Read the documentation carefully. With TypeScript being a superset of JavaScript, it’s quite easy to get lured back into JavaScript ways.
Overall, our experience with TypeScript is very positive. Static typing caught numerous bugs in the early development stage, which would otherwise be caught much later in the process or even sneak out into the production environment. Being a little bit more verbose for a good reason, it means there will be cases where some extra code will need to be written, but we believe that in most cases, the pros outweigh the cons.
Want to chat more about how we can help you elevate your tech venture with our web development services?
Get in touch at sales@dlabs.io.