To Type[Script], or not to Type[Script]?

Code and coffee
Photo by Artem Sapegin on Unsplash

About a year ago my team and I ported our application to React and TypeScript. Since then, we’ve also rewritten several of our other vanilla javascript applications with the same stack, which has given me quite a bit of experience with TypeScript. In the past couple years TypeScript has grown in popularity, and we’ve also seen the rise of other type checkers like Flow.

If you’re used to dynamic type systems, you might wonder why anyone would want the restrictions and boilerplate that static type systems inevitably carry with them. On the other hand, if you come from a Java/C#/C++ background, the Wild West of Javascript may seem unmanageable and foreign. Either way, you can’t deny that TypeScript has gotten a lot of attention in the front-end development scene, and I want to share my opinion, as someone that has spent quite a lot of time with this language, on the benefits (or lack thereof) of TypeScript.

Hold up, isn’t Typescript mainly for Angular apps?

Nope.

While Angular 2+ is written in TypeScript, there is an extensive community that’s continually working to make sure all your favorite libraries and frameworks work well withTypeScript. I’ve used TypeScript with React extensively and I’ve never felt like the language was a second-class citizen. This post isn’t going to be a tutorial on how to set up TypeScript with <insert-your-favorite-framework-here>, but I guarantee there are many such articles elsewhere on the internet.

A Little History

First released in 2012,TypeScript boasts better support for large-scale applications than JavaScript. TypeScript is a superset of JavaScript, meaning all of your ES6 code is valid TypeScript code, and it compiles into regular JavaScript. TheTypeScript website markets TS as “Javascript that scales” <insert link to TS homepage here>.

This is really the allure of TypeScript — all the benefits of Javascript (well, aside from dynamic typing, if you consider that a benefit), plus static type checking, advanced refactoring capabilities, generics, enums, and any other features that the TypeScript team adds to the language. That last feature is a really cool benefit of TypeScript — the TS team can add new features to the language independent of what is being adding to the next version of ECMAScript.

Last year string enums were added to the language, and I have to say that I love this feature.

Admittedly Babel can provide a similar role, and if you’re not using TypeScript you’re probably already using Babel.

That said, whether or not TypeScript is a good fit for you and your team really comes down to what you want from your tooling, so let’s explore some reasons you should consider it.

Type Checking

The most obvious benefit to using TypeScript is the static type checking it provides. This makes it very clear when an API changes that could break your code.

Let’s take a look at an example of this in action

First off, we create a type called Person, that has three properties for firstName, middleInitial, and lastName. When I create the me object and try to pass it to printName, VSCode will complain that my types don’t match.

TypeScript compiler error

Since TS can infer types from the shape of your object (see, duck typing), I can just add a middleInitial property to me without having to explicitly define the type of me as a Person.

Error Studies

Rollbar did a survey of thousands of JavaScript apps to determine the most common errors thrown.

Rollbar's study of errors in JavaScript projects

The results show that 8/10 of the most common errors are TypeErrors.

Now, before you jump to the conclusion that this could be completely solved by introducing TypeScript to these projects (which may be true to some degree), I would argue that it could also be solved with tests. It’s hard to know the state that these projects are in since Rollbar didn’t provide any more details. Some might have a lot of tests, some might have none.

Could you use types and tests?

Absolutely.

But types are not a substitute for good tests.

Refactoring

The ability to easily refactor your code is a great feature that TypeScript brings to the table. In VSCode, if I want to rename a property on one of my interfaces, all I have to do is highlight the property, hit F2, and voila! That property is renamed across my entire codebase.

Without TypeScript, you can easily rename variable or function names, but it is a lot harder to quickly rename an object property across your entire project. Plus, since TS will give you type checking, you can immediately tell if anything went wrong.

Immediate Feedback

My favorite feature that TypeScript provides is probably the fact that you get immediate feedback with every line of code. If I try to pass arguments to a function that are of the wrong type, VSCode will underline my errors right away. For instance, in the code from my example earlier, I can immediately know that the argument I’m passing to printName is incorrect:

Visual Studio Code's immediate feedback for TypeScript errors

In addition, it’s great to be able to hover over the definition of function and get a list of all the argument types that it expected. This is incredible helpful when you’re dealing with a third-party API and you want to be able to check what options you can pass to a function/class without going online to find the documentation. Not to mention when writing React components, this is extremely helpful to provide autocomplete-assistance for the props you’re passing.

Potential Downsides

Unfortunately, all is not happy-go-lucky in the land of statically typed JavaScript, however, as TypeScript also brings along some baggage.

Boilerplate and Visual Clutter

Perhaps one of the most obvious downsides to using TypeScript is the boilerplate. In my snippet above with the printName function, we had to add 5 lines of code just for types, which is as much as many lines as the rest of the code that is executed. Yes, this is a basic example, but in large applications you’ll end up with tons of interfaces and type definition files that clutter up the code base a bit.

If you want to see how this affects a React project, check out this example project I made.

TDD

My experience has been that Test-Driven Development is somewhat cumbersome with static types. It’s hard to get that “Red-Green-Refactor” workflow going when you can’t even compile your tests because TypeScript is complaining that your functions/types don’t exist. Of course, you can just mock out your interfaces and function definitions, or just go crazy marking everything with as any so that TypeScript doesn’t complain, but I much prefer the TDD workflow in regular JavaScript.

Functional Programming

As someone who really loves functional programming and tries to write functional JavaScript as much as possible, I find that TypeScript gets in the way sometimes. Ramda actually has pretty good type definitions, but lodash and many other FP-friendly libraries don’t. Doing things like function composition and currying don’t always make it easy to have perfect type coverage.

For instance, if I’m using the prop function from lodash to pick a property form an object, TypeScript cannot infer the type zip.

Now, start composing several functions like this, and all your precious types will be lost.

The only way to get around issues like this is heavy use of generics or to sprinkle a lot of type castings in your code, which brings me back to the issue of boilerplate and visual clutter.

Verdict

My goal with this article is not to pick a winner, but to show the tradeoffs as well as merits of choosing TypeScript. TypeScript is a tool that should be used in certain circumstances, but it does not always make sense. As far as I’m concerned, whether or not TypeScript is right for you or your team depends on two things in particular:

Preference

As I mentioned at the beginning of the article, a lot of people will already have a certain bent for or against type systems based on previous experience, and that’s totally ok. As with many things in software development, there are many ways to accomplish a singular goal and not one of them is the “right way.”

PS – The exception to this rule is the tabs vs. spaces debate. There is a clear winner here. You know what I’m talking about. </kiddingnotkidding>

Team Experience

This is related to my first point about preference, but on a larger scale. In a team environment, you need to be conscious of the experience and environment of your team. For instance, my team was very experienced in Java and had little experience in JavaScript or other dynamically-typed languages, so picking TypeScript was a no-brainer for us. If your team has a Rails back-end and is comfortable in the JavaScript ecosystem, then maybe TypeScript isn’t the right choice for you.

TypeScript is a great pick for my team, but it may not be for your team. Personally, I don’t use TypeScript often on my personal projects, but I recognize the merits it brings to my team.

There are sometimes strong feelings on whether or not TypeScript is beneficial, but I would encourage everyone to spend some time with it before deciding. As software developers it is important to know what tools we have at our disposal and when those tools should be used.

4 thoughts on “To Type[Script], or not to Type[Script]?

Leave a Reply

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