After two years with TypeScript – was it worth it?

Almost two years ago I started a new greenfield project in a small agile team. Microservices, docker, react, redux, all that fancy stuff.

I had some experience with similar front-end technologies, because I had spent the previous year on a pretty big react application written by over 20 front-end developers. It was quite challenging for me. We had a lot of issues back then: problems with models cohesion, growing code base, complex and hard to maintain api’s, inconsistent interfaces, hard to track runtime exceptions.

I decided to find some solution to those issues before starting the new project. I thought that maybe we had problems, because the language itself is a little bit too elastic and forgiving. You can type almost everything and because there is no compilation stage, no constraints and pre-runtime code validation, your bundle might contain severe bugs.

Then I’ve encountered Flowtype and TypeScript. After short evaluation I decided to go with TypeScript and now, after two years, I can tell you – that was a good choice for the project and my career. But if you think that the life of a TypeScript developer is a bed of roses you’d better read on.

In this article I don’t want to elaborate about TypeScripts’ features or dive deep into project setup. There are lots of great resources about that on the Internet (for example official TS documentation: https://www.typescriptlang.org/docs/home.html). It is not an introduction for the beginners either.

It is a story about upsides and downsides of everyday work with TypeScript. I would like to describe my worst experiences with TypeScript and, on the other hand, features I consider the most useful. It’s about pros and cons, tradeoffs and benefits, so let’s start.

First things first – setup

As I mentioned, I had some experience with react and redux, so I wanted to take advantage of that and use similar (custom of course) setup in new project. Usual stuff – webpack, babel, npm scripts, jest, linter. Just one little thing extra – TypeScript support.

If you are in a similar situation right now – I can assure you: it is not just another loader entry in webpack config. I had to provide a dedicated config for TS, replace ESLint with TSLint, integrate the TypeScript loader and babel, plug in TS into Jest (test platform).

Some disappointments came in very fast. TS configuration wasn’t so obvious and “just copy that and test” strategy is not the best way to prepare well performing setup.

You’d better read the documentation carefully before even placing tsconfig.json in your project.

Moreover, there were some issues with Jest (transforms, module mappers) and the css module. Probably you will face them sooner or later. And don’t think, that at least you can abandon babel now – TypeScript won’t provide any polyfills for your new shiny features, won’t transpile your new API’s to code that is understandable by older browsers.

So my advice is – if you can, you should use some  starter kit or CLI supporting TS out of the box (like angular cli) to spare yourself that neverending story with a project configuration.

Libraries support

Another pretty unpleasant experience is related with the number of libraries supported by TypeScript.

Typically, when you are an author of a npm package, you provide ready to use valid JavaScript bundle. Sometimes, you also expose ES6 source of your package. If you want to prepare your library for TypeScript, you have to provide type definitions. In simple words – the file with a declaration of every module, namespace, class, method, function etc that will be available for TypeScript consumers. A TypeScript module can use only things described in your definition and only in a way specified in the declarations. Unfortunately, usually there is no strict connection between source code and declarations. They simply may be incorrect or outdated. Or missing.

Personally, I had no problems with missing declarations. Most popular libraries have their own types definitions prepared by authors or community. If there is no such file for a package you use – use another one, there are dozens of packages doing the same thing in NPM registry. You can also prepare “dummy” definition or create real one and contribute to the open source community.

Anyway, there is a much more serious issue – as I mentioned already some declarations are incorrect or outdated. If you face such problem, there is no simple solution. You could either stay with an older version of the package with a working declaration, fix them by yourself and contribute or wait for the authors. Sometimes they react quickly, sometimes they don’t.

By the way, I’m the author of a few simple packages and believe me, even with good will, I used to forget about syncing new features with their types definitions.

Everyday work

Now it’s time for the good parts. Once you had configured your project and picked up libraries with decent TS support it’s time to embrace the power of the typed language. If you have no background with such languages, it might be a little strange at first. There are a lot of features in TypeScript that you can’t find in current JavaScript syntax. Let’s talk about few of them, which for me are the most useful ones.

Types

Unsurprisingly, the most frequently used feature of TS are static types. There is no sense in using TypeScript without strict typing. Of course you can use forgiving “any” type, which means “I don’t care about the type of that thing, it could be a number, it could be array of strings, just use it”, but seriously – better go back to good old JavaScript if you want to code that way.

Types will help you code faster and safer. You can tell the compiler that “this constant will point to a number” and TS compiler will always prompt you with an error, if you try to use it like an array or a string. Basically, you can still do whatever you want with your entities, just like in regular JavaScript, but now your manipulations are much safer and more understandable than before.

There are several basic types in TypeScript and few advanced types and techniques related to them.

Below you can see some basic but really powerfull stuff, for more advanced types please visit documentation: https://www.typescriptlang.org/docs/handbook/advanced-types.html.

Apart from well known types like numbers or strings TypeScript offers also enums.

You can use builtin types like Date or Error. Embrace code tips for faster and safer programming.

Interfaces

If you think that types are “the game changer”, what would you say about interfaces? Interfaces help you write much better code, because they finally allow you to define contracts between objects. I create a lot of interfaces. They are everywhere. Sometimes I have dedicated file, just for interfaces, because they deserve that.

I use them mostly for describing shapes of the objects, classes, functions and arguments. You can share them between modules and treat like real entities in your source code, just remember – there are no interfaces on the runtime, sometimes it is easy to forget about that. That’s why there are cases where it is better to use a class instead of an interface (like with Angular Dependency Injection). Let’s see some real life examples with interfaces:

On the left – incorrect implementation of returning type. On the right – VS Code immediately informs about errors in your code.

On the left – a class incorrectly implements the Driver interface which extends User interface (see previous screen). On the right – descrciptive error information..

Classes

There are classes in ES6, so you probably used them before. But in TypeScript classes have some additional features, which presumably will appear in future implementations of EcmaScript. In TS you can define abstract classes, you can describe a class property as static, private or read only, you can extend classes and have classes implementing interfaces (that’s right!). I won’t describe differences between TS classes and ES6 classes, because at the end of the day they both result in similar JavaScript code (after compilation and transpilation). In TS classes are just elegant and efficient way to encapsulate responsibility and they are very similar to other languages implementations (like Java) which has some repercussions (more about that in “code review” section). Checkout that examples to see how easy your work could be with TypeScript and good code editor:

Of course there is a lot more new things in TypeScript, like generics (you will use them), enums (meeh, sometimes maybe, for internal things), namespaces, JSX support etc. But you don’t have to know and use them all at the beginning, just play with basic features mentioned above and you will see, that quality of your code will improve.

With TypeScript you can use features like abstract classes. More about abstract classes: https://www.typescriptlang.org/docs/handbook/classes.html

TypeScript supports private, public and protected methods, read only properties. Classes can implement interfaces or extend other classes.

Code quality

Did I just mentioned code quality? Of course I did, because we all care about code quality (and client needs, and deadlines, and estimates, and…).

So why exactly should you use TypeScript (in a context of code quality)?

  • code is  free from some very annoying runtime bugs related to typos in names of arguments or variables
  • you can establish clear and well known contract between objects
  • there are no hacks needed to achieve such things like private members of classes
  • there is instant feedback from the compiler, a lot of errors are caught on compilation stage, not on the runtime
  • code is easier to read and understand by non-JS developers
  • you can use features from future versions of JavaScript
  • it is much easier to write mocks, stubs and fakes for unit tests, because you know theirs exact interfaces

Moreover, because of excellent IDE support, it is much easier to write maintainable code. Let’s be honest – even when you are working alone on small/medium size application, after few weeks you won’t remember what types of arguments you have to pass to a service or what is the shape of newly created user. You can go to the source, examine the code and extract valuable information (because your code is always clean and fast to read), but wouldn’t it be better to just hover over the function name in your favourite editor and see for example, that it accepts “age”, which is a “number” and returns User instance with “age” and “name” properties?

Code review is another thing I want to mention. When you are working in small team sometimes you are the only front-end developer and your teammates from .net or Java really don’t appreciate looking on vanilla JavaScript. It is not very readable for them, because of dynamic and laconic nature of the language. No types means no tips. For example – object with name “user” has “ID” property, but is it a number or a string? If a string, why you just called “toString()” on it? If a number, why the hell you just added string “id_” in front of it? TypeScript code looks a lot like other popular typed languages and there is a chance, that you will receive better and more precise code review of your commits. And better code review simply means better code. Period.

On the left – code in TypeScript. On the right – code in Java. As you can see, syntax is very similar, which means, that your code should be easier to understand for Java developers than vanilla JavaScript.

Learning curve

Oh, at the end I have one more word to say about TypeScript. As always, when you are trying something new there is some learning curve. In the case of TS it is not exactly steep, but steep enough to avoid starting with TypeScript and with a new framework. Especially in large or inexperienced team. That’s why I have chosen that project two years ago for my first TS application – I knew the stack pretty well, so it was a great chance to learn a promising language. I’m pretty sure, that if I had chosen a new framework (let’s say Angular) and a new language at the same time, I would be much more disappointed and frustrated.

Summary

Would I recommend TypeScript to you? Of course I would. It is going to help you deliver better code in shorter time. The IDE support is great now, the community is vibrant, the number of libraries with TS definitions is big enough and growing, the programmer experience (fast feedback from compiler) is pleasing. This is the best tool I know for creating modern and scalable web applications (and Node.js services too!). Just bear in mind some cons mentioned above, deal with them and deep dive into colorful world of statically typed languages.

Author:
Kamil Zagrabski

Share:

Share on facebook
Share on twitter
Share on linkedin

Copyright © 2020. ecom sp. z o.o. All rights reserved.