image

The Business Case for TypeScript

It's no secret that developers chase fads, and that fad-driven development leads to codebases haunted by bad decisions at every corner. Ruby on Rails shook the world when we discovered you could build an entire working blog in 15 minutes, but it turned out to have strengths and weaknesses so we became disenfranchised with it. Angular came out and everybody was entranced by the magic of two-way binding, only to loathe it a year later when it turned out not to be a panacea. Everybody started writing React and rode the waves of mixins, higher-order components, render props, and now hooks as we jumped to the new Right Way™️ to extract common functionality. This phenomenon is not restricted to front-end development or even to software development as a whole, but it seems to be especially pronounced in the JavaScript world. A little caution to temper the hype would go a long way.

In a meeting the other day, a coworker asked about strategies to transition a plain JavaScript codebase to TypeScript without losing one's mind. A few folks weighed in, but then someone decided to ask that question: "Is it actually worth the effort? Is the time we're going to sink into transitioning a codebase worth the opportunity cost of features we could deliver during that time?"

Maybe you're on the TypeScript train and you find that question triggering because you've heard it a million times before and your heart screams, Yes! It is worth it! I could never go back! Maybe you feel like you're the last person in the room who hasn't yet drunk the TypeScript koolaid, and, to mix my metaphors, you're wondering if the emperor has any clothes on. So let's do it: let's poke and prod this question and see if there's a business case to be made for TypeScript.

The Business Case For TypeScript

There are a few major reasons you might want to consider using TypeScript: reliability, enjoyability, and hospitality.

Reliability

This is the most common argument for TypeScript: it'll help you catch more bugs before your app hits production. We'll trot out a familiar and trivial example, which is that JavaScript will happily let you add a number to a string. It's the only time that 3 + "3" === "33" , but of course that's probably not what you really meant in all likelihood. If you're a seasoned developer, you'll rightly respond, "But that's an obvious error! Anyone would spot that, and making me explicitly say that these are supposed to be numbers just wastes time and is just a big annoyance!"

An example of TypeScript catching a very basic type mismatch before running the code.

True, and that's why using a trivial example to pitch the value of a production-grade type system is not all that meaningful. It would be helpful, though, to have a tool that would keep you honest when you're pulling in a couple functions from a other modules and piping complex data through them and maybe using a couple functions from packages off of npm, and that's where TypeScript has shined in my experience. Static type systems like TypeScript (or Facebook's Flow) can conservatively catch around 15% of bugs that seasoned developers inadvertently let slip into their code. If your team had an extra 15% time to ship features instead of fixing bugs, that would be nice, wouldn't it?

Catching bugs at compile time or even at development time with editor plugins is a huge win, but there's a big caveat there in my experience. Gradual type systems like these are convenient, but the tradeoff is that they're only as good as you configure them to be combined with how much you cheat the system. Annotating variables as any , for example, cuts type inference off at the knees and forces the compiler to accept whatever shenanigans you throw at it. There are times and places for using any , but you're effectively just writing plain JavaScript again at that point.

It is absolutely true that it's pretty irritating to have a type system holding your feet to the fire when you're just trying to sketch out an idea, and so it's pretty tempting to tell the compiler to just shut up and trust you by using any , // @ts-ignore , foo!.bar , and other tricks. But like good documentation or a robust test suite, a helpful set of types isn't magically created out of thin air overnight—if you don't make it a habitual piece of your development flow, it won't ever happen and you'll keep any -ing your way out of every jam until the prospect of getting your codebase in shape is functionally impossible.

tl;dr: TypeScript really can quantifiably make your code more reliable if you configure it well and don't cheat the system.

Enjoyability

I learned HTML, CSS, and JavaScript by writing it in Notepad on Windows XP, saving the file, and then refreshing the page in Internet Explorer to see if it did what I expected. It was magical and enjoyable, even though Notepad did absolutely nothing for me by way of indentation, syntax highlighting, or many of the other tiny little things that I have come to appreciate. When I heard about Notepad++, it was a revelation that a computer could do things to help me program better.

Since then I have gone through more text editors than I can remember, configuring each one along the way with settings and plugins that suited my needs. For a long time I was writing JavaScript in editors like Sublime, Atom, or VS Code, and my workflow was to alt-tab back and forth between my editor and a browser window full of documentation tabs: MDN, Lodash, moment, React, etc. I'd be in the flow of my code, and then I'd forget whether it was splice or slice that I was needing so I'd have to go look it up. Then I'd forget what the order of arguments was for React's componentWillReceiveProps() lifecycle method. Then I'd realize I needed a function here that would take an array of users and partition it up according to their age group. Is there a lodash method for that, or am I going to have to write my own?

An example of TypeScript's usefulness as a developer productivity tool.

When I got into TypeScript and started using VS Code, I quickly realized how amazing a productivity tool the TypeScript Language Server is. Regardless of whether TypeScript catches bugs or not, I almost never have to go to look up docs anymore. With autocompleted methods and imports and variable references, I can kind of just type what I'm thinking and see if I have something at hand that solves my problem. If I can't remember what a function does, there is an inline markdown-formatted description for me right there in the autocomplete menu. It shows me the name of the argument I'm currently trying to type in. Sometimes I have to work with a poorly-documented library, but since I have type information I can figure out what I need to do without having to go source diving on GitHub. It's not that I never have to go look at docs anymore, but it's much easier to stay in the flow and get my feature done quickly.

With type information, the compiler can know the interrelationships of variables a little more confidently and thus the refactoring tools are a lot more powerful. The TypeScript team has worked to make some of this magic available in plain JavaScript files, but the overall development experience feels kind of like using one of those crummy, cheapo Android phones after you've used a Google Pixel or like eating an off-brand hot pocket from the dollar store after having had a calzone from an authentic Italian restaurant.

tl;dr: TypeScript can be a really enjoyable development experience that helps keep you in the flow of programming instead of having to alt-tab around various docs.

Hospitality

Is your codebase hospitable to contributions from newcomers? How discoverable is it for a new employee? How easy is it for someone from a different team to file a PR on your repo if they need to without inadvertently breaking stuff or doing something wrong? I was recently in this situation at work. I had to file a PR against a repo that was written in plain JavaScript, and I needed to take some data from one object and transform its shape into something else before sending it along its merry way, but for the life of me I could not find where the shape of that object was defined. What were the names of the keys? What types were they? Did I have to worry about nullability? After a lot of sleuthing around the codebase, I was able to fairly confidently infer what the shape was, but I couldn't help but think about the fact that if it were TypeScript I could cmd+hover over the object and I would've had the answer to my question in milliseconds rather than hours.

On another occasion, I had to submit a PR to a different repo that was written in a Go, a language I do not know. It's a statically-typed language, though, so I could pretty easily determine that this needed to be a float and that needed to be an int and the other thing needed to be a string. Again, I don't even know the language, but the code was compiling and tests were passing within a couple hours on a ticket that was supposed to last me a few days.

I want to suggest that hospitality is another angle with which to examine the business case for TypeScript. Just like documentation and tests can help newcomers help themselves, so too can static types be a sort of living documentation about what the code does and how to use it and extend it. Sometimes I can get annoyed that I have to expressly specify the shape of my data when dang it, I know what I'm dealing with here just get out of my way and let me do my work, but the reality is that it is a given that someone at some point in the future is going to have to know about the shape of your data and what its rules are, and they will not already know what they're dealing with. Writing type information is a way to pay it forward to the next developer.

tl;dr: TypeScript can make your codebase more hospitable to contributions from newcomers.

The Business Case Against TypeScript

This will probably be odd, but now I'm going to give you a few reasons and scenarios in which I would actually recommend you do not adopt TypeScript.

It's the new hotness

TypeScript gained a lot of hype and new users in 2019, and for good reason. The TypeScript core team has been killing it, and the language continues to improve at a pretty steady clip. But just like smoking cigarettes or buying an instant pot or snuggie, adopting TypeScript is not something you should do solely because everyone else is doing it. That's simply not a good enough reason to start doing something. Why? Because adopting something costs something. You should expect your development speed to dip when you start writing TypeScript; it looks like JavaScript, but it's a different language and there is a learning curve. If you've heard other people praising it and you've played around with the examples on the TS Playground , and you keep getting curiouser and curiouser, maybe take it for a spin in a test project that you can throw away: yarn create react-app my-app --template typescript . But for goodness' sake, do not start porting over your production app to TypeScript when you remain unconvinced. There will almost certainly be a negative ROI for you.

You want it, but nobody else does

Look, we've all been there: you're convinced that the new hotness is not just a passing fad and that it really does have legs. The trouble is, nobody else on your team agrees. If you think TypeScript would improve the reliability of your app, the enjoyability of your development workflow, and the hospitality of your codebase, but your team sees this as a big pointless waste of time, then do not do it. It will not be worth it. The good news, however, is that while you may not get to drive the Porsche to work, you can grab a roll of orange duct tape and tape some racing stripes on your Honda Accord to go a little faster. What could such a bizarre analogy even mean? By annotating your normal JavaScript files with JSDoc blocks and by adding a .jsconfig file and // @ts-check comments you can pay it forward to future developers by documenting type information and VS Code can even use JSDoc annotations to help you out a little. It's not the same as having a TypeScript codebase, but your coworkers can't really complain too much and it's at least something.

Though it is not quite as nice an experience as actual TypeScript, the TS language server can help out quite a bit with plain JS using JSDoc annotations.

Your plan is to write Java-in-JavaScript

If you're a Java or C♯ developer, sure, TypeScript looks a lot like your favorite statically-typed OOP language… but that doesn't mean it's the same language or that you should try to act like it is. The trouble with writing TypeScript that way is that it goes against the grain of what the language actually is in the attempt to be something that it's not. JavaScript is not Java, and neither is TypeScript, even if it has keywords like public and private and abstract .

TypeScript has some of the most innovative, interesting type features of any production-grade language right now, and it has the utilities to encode your business domain quite nicely at a type level. I use features like discriminated unions, conditional types, and mapped types quite regularly to be able to go beyond guarding against adding 3 + "3" and to fail my build if a business rule is broken or to make sure I've handled all possible cases instead of just the happy path.

Conclusion

At the end of the day, here's a simple rule of thumb for you: if you and your team don't like TypeScript and you think it's just a big load of hype, don't adopt it. You don't even have to log onto twitter to tell everyone that you're not adopting it; you can just cheerfully continue writing vanilla JS. If you do think there's something to it, dip your toes in the water and take the time to read up on the language and what it has to offer. If you decide to adopt it, then lean in and don't get sucked into the mindset of "appeasing the compiler," but figure out how to sand with the grain. Lean in and get excited about it! Lean in learn the language as well as you know JavaScript. Lean in and learn how to use static typing as effectively as you know how to use dynamic typing. If you're currently writing TypeScript, help other people learn it! Push yourself to write the types you'd want someone else to give to you, and may your codebase be ever more reliable, enjoyable, and hospitable.