My experience using TypeScript

We’ve talked before about how we decided to use React for our front end development work. You can read Rob’s description of why we chose React in our previous article here. However, we are continuously looking to improve our development practices here at Waterstons, and recently I’ve begun to use TypeScript with React to provide a richer development experience. If you read on, I will explain my findings having used TypeScript with React for close to 9 months now.

What is TypeScript?

According to https://typescriptlang.org:

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.

It’s a great place to read about TypeScript. They have some quick guides, tutorials, and documentation to help you get familiar. However that phrase above only begins to detail what TypeScript is.

TypeScript brings to JavaScript several additions, though the most notable is types (through types and interfaces). However, you need to move out of the object oriented world here. We aren’t talking classes and objects, we are talking specifically types. Think documentation for your code that produces compile time errors for certain coding issues and you’ll be far closer to what TypeScript is.

JavaScript is known for being a very flexible language, so you may think adding types to that removes a lot of the flexibility. However, TypeScript manages to maintain the vast majority of the flexibility, and brings that flexibility to typing your code.

Why TypeScript?

There is a spectrum of choices when it comes to JavaScript. From pure JavaScript offering no type information, through to TypeScript and beyond.

Pure JavaScript
This is what we had been using. Personally, I love the flexibility of JavaScript, and the creative ways you can use it. I was sceptical of anything that tried to add types and seemingly limit my creativity. However, there are flaws. To find out what properties your React component has, or parameters for your JavaScript function, and which are required, you had to look at some documentation, or even the source code, and depending how it was written, it still could be hard to find. It was possible to make many mistakes here, and not discover them until runtime.

External Documentation
Readmes, or well constructed documentation pages are good, and help go a long way to solving some of the problems above. But you need to remember to update them, and it still takes you out of the code to find the answers to your question.

JSDoc
Another good solution, JSDoc (http://usejsdoc.org/) defines a standard comment format for providing information about your code. You can describe parameters with type information, and give good helpful information that is picked up by good code editors, and used for intellisense or similar (something that TypeScript doesn’t do). However, it’s wordy, and personally I’ve never liked large comments, as they spread your code out (see Clean Code book by Robert Martin for an excellent explanation why this is bad). And as always with comments, any changes to your code means you have to update the comments, and make sure they move with your code. You won’t get any compile time errors telling you your documentation is incorrect either.

Flow
Flow is an interesting alternative to TypeScript (https://flow.org/). It essentially solves the same problem in a similar way. It was developed by Facebook. Both Flow and TypeScript are effectively competing in exactly the same space. However in recent times, TypeScript is beginning to gain far more traction. The State of JavaScript survey shows huge interest in TypeScript (https://2018.stateofjs.com/javascript-flavors/overview/) with over 70% having used and would continue to use, or interested in using, compared to under 35% for Flow. In addition, Jest, the JavaScript testing tool from Facebook, has also recently migrated from Flow to TypeScript, so really TypeScript stood out as the likely way forward.

TypeScript
TypeScript gave us almost everything the above options gave, except descriptions from comments, in a nice clean and clear format, built into the code.

Beyond TypeScript
It is worth mentioning there are other alternatives beyond TypeScript. One such example is Dart, the language created by Google. It is possible to use a compiler to compile Dart into JavaScript, and will give you a fully typed language. This may be a good option if you are used to the Dart language, and would like to write front end code, but brings it’s own obstacles (steeper learning curve, time to implement new features), and for us didn’t pose any advantage as it would be an entire new language to learn, so was out the picture.

The Benefits of TypeScript

As discussed, TypeScript gives us type information for our JavaScript code. We can define types for parameters, variables, function return types and more. These can be built in types, custom defined types, or types imported with external libraries.

However types in TypeScript are descriptive rather than explicit, and we still have one of my favourite features of JavaScript, duck typing (https://en.wikipedia.org/wiki/Duck_typing). As long as our object has all the same properties with all the same types, it can be treated as if it was our type (even if it has additional properties on the object, not on our type).

Duck Typing — Photo by JOSHUA COLEMAN on Unsplash

TypeScript also has type inference, meaning we don’t have to explicitly define types where it’s obvious. For example const myNumber = 6; will be typed as a number for us without any declaration. It can also pick the closest common type, when multiple different types are used, or assign the union of all types used. See https://www.typescriptlang.org/docs/handbook/type-inference.html for more information.

And that’s something else I haven’t talked about yet. The ability to union types adds some really interesting options, particularly when dealing with strings as parameters for react components. Consider this interface:

interface Arrow {
direction: 'right'
| 'down-right'
| 'down'
| 'down-left'
| 'left'
| 'up-left'
| 'up'
| 'up-right'
}

Then when I try and use the Arrow component using this interface for props, I get the following intellisense:

Intellisense on valid strings from TypeScript

The above would also fail to compile if I chose a different value, not in the list.

TypeScript also brings with it an enum type that can be useful if you would like to be more explicit with this sort of typing.

Are there any downsides?

So, I’ll admit, I initially tried TypeScript two years ago, and hated it. I pulled it out of the project that had it include by the default template, and stuck with using plain JavaScript.

The main issue at the time was external libraries. Support for TypeScript was not great, and external libraries would bring issues with lack of type definitions. TypeScript didn’t particularly like this scenario and would complain until I generated types (a fairly awkward process).

Fast forward to today, and this issue is greatly reduced. Thanks to DefinitelyTyped NPM packages (https://github.com/DefinitelyTyped/DefinitelyTyped) there is a wide range of type definitions available for libraries that don’t provide them out the box, and this is only increasing. It is rare that I want to use a library that I can’t get types for, and with an increased understanding of TypeScript, I can now decide whether I should generate types for the library, or just leave it without types. Don’t get me wrong, I still find it a pain to generate types, but it’s rarely required.

There are also some rare scenarios where the types don’t work. I’ve got a fancy React Component, that has dynamic properties, and there is no way for me to tell it what type those dynamic properties are going to be (at least to the best of my knowledge), even though I know everything starting with ‘on’ is going to be a function.

In addition you still have to be careful when getting data from an external provider (e.g. an API). I came across a scenario where a property was typed as a Date, and the code compiled fine, but the external API returned the date as an ISO 8601 string. TypeScript isn’t going to convert this to a Date, that’s not what it does. You still have to make sure you are providing the right types for the data you are going to get. In addition to this, because your compiled code is not strongly typed, you won’t even get a parsing error, like you would in C#, you’ll get an error elsewhere when you try to use the property, which can make this sort of error tricky to track down.

Another challenge is getting people to think outside of an OOP world. TypeScript looks similar in many ways to our preferred language for server side: C#. However it behaves very differently. There is always a challenge getting people to change their mindset from C# to JavaScript, and the similarities in syntax between TypeScript and C# only makes this harder. However this is very much a training issue, and not one for TypeScript to solve.

Closing Remarks

TypeScript has been excellent so far. After my initial experience two years ago where I found it quite awkward, the uptake of TypeScript has improved greatly solving many issues, and so has my knowledge of TypeScript helping me to overcome others.

Something I haven’t mentioned so far, but is also important, is it’s not all or nothing with TypeScript. Gradual adoption is possible. One of my colleagues has switched to use the TypeScript compiler with JSDoc to provide type information as he’s working on a project that was already extensively using JavaScript. You can also mix JavaScript and TypeScript, or even just use the TypeScript compiler for type checking, and use Babel for stripping out TypeScript declarations at build time.

Overall, TypeScript has led to cleaner code, that self documents, and has provided compile time catching of certain errors. I’m going to look to roll out the use of TypeScript across our new projects here. The benefits have definitely outweighed the occasional issue, and finding all the new flexible ways TypeScript types can be used, combined, or created is fast becoming a passion of mine!