The Bad and the Good reasons to reject TypeScript.

Mozafar Hayder
10 min readNov 29, 2022

--

This article is a continuation of The fallacies of “the TypeScript tax”.

Bad reasons to reject TypeScript

Testing is more important than types

These two “testing” and “types” are not in a direct competition.

TypeScript does not replace testing. In practice, TypeScript often makes writing tests easier, for example if you already have a type for the data returned to a component from an API, you’re already third-way in writing the test by knowing what the Arrange part will be (in an Arrange-Act-Assert pattern).

But that’s the theory. In practice, when developers mention this argument, they are aware that the two are not mutually exclusive. What they mean is that — within the specific context — there are pressures (higher management, deadlines, stakeholders) that mean that we have to sacrifice some quality measures in favour of others.

I can’t argue with that — I have been there. So I will appeal to the Pragmatic Programmer within you.

This type of situation and projects are where TypeScript is very important. Because, in practice, when that pressure mounts, the first quality measure that gets relegated to the backlog is “tests”. It becomes tech debt that never gets tackled, because — in this type of organisation — the pressure never stops.

Types are harder to ignore than Tests. With TypeScript, types are in your face, you actually have to take an active action (to mark an argument as any for example) to ignore types, while you can passively ignore tests avoiding any sense of tech guilt or responsibility.

Even if you start having any everywhere, you will — at least — get the easy types in and some of the inferred types. And this is a great place to start given that the alternative is no tests and no types.

The extra time to write and maintain types

While TypeScript does add time, that cost should be considered in different lenses:

The value types add

In an extreme situation where you have bad test coverage, TypeScript provides some level of confidence in the code. In ideal situations, where you have good test coverage, types add an extra layer of safety and can make refactoring tests and implementation easier.

Tests too add time to write and maintain

It is imprecise to imply that only types have a time cost. Tests also take significant extra time to maintain and write, even more than types in my experience. If you are working in an organisation with a decent test coverage, then you must have hit a situation where you’ve written a feature in minutes then spent hours writing or updating the test.

The next point is related as well. In a real-life project, patterns start emerge quickly drastically reducing the time to write types.

Patterns emerge quickly in TypeScript

While TypeScript is vast, in practice, patterns start to emerge quickly for most of your use cases when you’re building a specific app. Let’s say we’re building a React application and you don’t know TypeScript, you will quickly learn how to define the props for a component (and I find them objectively easier to remember than propTypes syntax), you will find that you’re always maybe defining a type for an API response, then consuming it through an API layer / library in the code, that already provides types. As a beginner, you will struggle a bit to understand the use of generics in useState maybe, but type inference will save you most of the time, then once you do it once, you repeat.

In practice, this cost goes down as you spend more time in a project. There will always be the odd type that will give you a hard time, but that applies to tests as well: there will always be the one change where tests take longer than the implementation.

Other reasons

Some “bad” reasons manifest in generic conclusions without much evidence. Try to question them, and see how strong they hold: If someone argues that “We rarely have type errors”, have a search in your commit history for the words “misspelled”, “undefined”, “null”, or “.legnth” you will likely find few examples — a better test coverage would have caught these, but since we obviously didn’t have that coverage, wouldn’t it be worth it to explore ways to avoid these types of issues. If someone argues that “code inference in the IDE is enough”, then show them the difference in consuming a React component written in Typescript vs one using propTypes, and if the followup is that “We should improve documentation”, then does it really cost less to maintain documentation than maintaining types?

Not-so-bad reasons for rejecting TypeScript

Avoiding the Hype

I do think that the decision to use TypeScript is often a sign of an organisation’s maturity, in the sense that it is going out of its comfort zone to improve quality. But it is also potentially a sign that an organisation is going out of its way just to follow a hype.

The hype road has many stops before TypeScript though: using a complex state management library without a reason, push for end to end automated tests for everything (and endlessly fighting the flakiness and rising costs), monorepos for the sake of it, waterfall presented as agile (scrumfall), TDD itself was a big hype and BDD still is, and the road goes all the way to microservices, microfrontends, kubernetes and more. All of these have a much higher cost than a language choice.

So is the conversation about TypeScript in your organisation following a hype, or a genuine quest for better quality?

The question — in my view — has a philosophical value (and it might be an indication for a deeper cultural issue in an organisation) but it has very little practical implication in the specific conversation about TypeScript.

If you’re going after TypeScript for the hype, then that doesn’t necessarily mean you’re taking the wrong decision — a wrong approach doesn’t inherently mean a wrong decision. Engineering solutions typically start from a certain problem, we identify the solutions, compare them then make a decision. But sometimes, the process is reversed, it takes a solution to see a problem. This happened for example with Functional components in React, no one complained about class components before but it took a solution (functional components) to see the problems and the complexity of the old way of doing things.

In the TypeScript case, you might have started from either end: by defining a problem and thinking that TypeScript would solve it, or by looking at the solution and wondering what benefits it would bring to you (followed the hype). I do not think either approach is wrong, as long as you do the research and analyse what it means — for your specific context — to move to TypeScript. An over-emphasis on the approach distracts from focusing on analysing the ROI of TypeScript, which should be the main issue at hand.

The decision to move to TypeScript is far-reaching

This is a half-truth.

It is far-reaching, but not from a technical point of view. The people impact is high: the impact on their confidence, the psychology of fearing change, the need to invest in re-learning and mentorship.

There are many far-reaching implications for a move to TypeScript, but the technical ones are not necessarily the most critical. The reason is that the cost of opting out from TypeScript — if it doesn’t work out — is negligible. TypeScript is a superset of JavaScript, and it would literally take one command to be back to JavaScript typeless land. Compare that with moving from Redux to a different state management library, and you might realise that, from a code perspective, TypeScript is far from the most far-reaching decision in your tech stack.

Onboarding is difficult

From a coding point of view, the hardest onboarding I had was joining a company where the main tech stack was Vue with TypeScript. I did not know either, and it was tough.

The “coding point of view” is only one side of onboarding though, and even though that role was hard technically initially, it was far from the hardest onboarding experience I had overall. The hardest experiences include a unicorn where literally no one talked to me for the first four days, or the corporate where I did not get a laptop for the first ten days then was told off for repeatedly asking for it.

Onboarding is hard, and very personal. It can make or break your experience in a company, and it’s hardly about the technology. It is not just about understanding the code or the tech stack: it is understanding the culture of the team, the documented and undocumented rules and lines of communication, the product being built, the patterns followed, and just having a general sense of the team.

It is also about having a clear set of expectations about what you need to do, and when you need to do it.

For that Vue and TypeScript role, the expectation was that I will learn them on the job. I was given the time and support to learn both technologies. If I was expected to be 100% productive from my first week, then I would have failed. And it would have been their fault.

This applies to TypeScript but also any other technology. I struggled the first time I had to work with a codebase using redux, I struggled (and still struggle) with redux-saga, react-query. Any opinionated library or new technology has a learning curve and it takes time to work effectively with. I do not think that TypeScript onboarding is particularly harder than these other technologies if (and it’s a big If) everyone is given the support and time to learn it.

The Syntax Noise

I come from a C# / Java background, and my mental model for expressing a type was to declare the type first then the variable name String name;. Something as simple as reversing this order to say let name: String; in TypeScript was very hard to accept. I was surprised at how a relatively small detail was so engrained in my head so much that, for the first few weeks, whenever I looked at TypeScript code, all I saw was visual noise.

This relates to the next point about the learning curve. But the tldr; is that you will get used to the extra “noise”. But if you — like me — only did other strongly-typed C-like languages before, then spend some time reading the TypeScript for Java/C# guide and it would make your journey to accept TypeScript much easier.

The good reasons to reject TypeScript

The learning curve

I admit that TypeScript has a significant learning curve.

I made a point before that, in practice, the subset of TypeScript that you would use in a project tends to be repetitive and follows certain patterns, so a bit of informed copy/pasting can get you around TypeScript initially.

But you will, inevitably, hit a point where you want to properly learn TypeScript.

For me, this is similar to when JavaScript and SPAs were getting popular. I came from a C# background, and for years, I assumed I can code JavaScript just because it looks a bit like C#. I used patterns and knowledge from C#, and constantly complained about JavaScript. Then, I read JavaScript: the good parts and I realised that I was coding JavaScript without really knowing JavaScript.

The same applies for TypeScript. My aha moment was when I read Effective TypeScript. In fact, my aha moment was when I just finished chapter three of the book. It clicked for me, and from that point, it was smooth sailing with TypeScript.

The point though is that I had to stop and learn it. After a while, it’s not enough to wing it. And if your organisation, or the circumstances and the pressures for the project, won’t allow for proper learning, then the learning curve is definitely a good reason to reject TypeScript.

Lack of a plan to move to TypeScript

“We are moving everything to TypeScript now” is not a plan of action. The main point of the Return On Investment conversation is that context matters. That context extends to individual projects not just individual organisations.

In a mid-size organisation, you might typically have a variety of projects:

  • Public-facing projects
  • Internal projects
  • Internal UI libraries
  • Shared utility libraries
  • Internal tools
  • Greenfield, Brownfield and Proof of Concepts projects

The ROI conversation needs to happen per project or types of projects. You might have a decision to use TypeScript for all new Greenfield projects, but then what happens with existing projects? Do we need to distinguish the approach between different types of projects? Maybe it’s worth it for some projects but not the rest.

The plan also has to include the people: How do we bridge the knowledge gap between those who know TypeScript and those who don’t.

The digital divide

Without a plan to move to TypeScript, you could end up with a two-tier organisation: some are “first-class citizens” who know TypeScript and get to build all the new shiny stuff, and others are left behind.

This is often a side effect of not planning the move to TypeScript, and the human side of it. It can leave the team divided, people feeling left behind, unappreciated and frustrated.

In my view, adopting a technology is as much a people’s problem as it is a technical one. So if you’re moving to adopt a new technology, then invest in your people — all of them — to make sure that they get the support and mentoring to get up-to-date with TypeScript.

That support, in the context of TypeScript, looks like this:

  • Give the time, resources and space to learn: in my case, this involved the company getting a copy of a book, but could be access to a training site, or just some time to experiment — people learn differently so adapt to that.
  • Create a channel/group for TypeScript: Beyond the basics, it is hard sometimes to know how to express an idea in TypeScript, or even how to ask the question to Google. It is often more effective to ask colleagues, and a simple pointer can save you hours.
  • Code Reviews: Good code reviews were key to learning TypeScript. Get into the habit of tagging people who know a bit more about it, this helps you both get better.
  • Pair programming: Even if you’re not a fan of pair programming, it is worth it to pair with other developers especially at the beginning.
  • A TS Champion: In my case, that was one work colleague — Juan Carlos Arocha - who took the responsibility to champion TypeScript and help others accept then embrace it. His role was informal, but he often reviewed PRs, jumped on impromptu calls to explain a point, wrote posts and notes about his new learnings, and he did it all with empathy and patience. This, in my team, had the biggest impact to move to TypeScript effectively. This is not always possible, and it’s a big ask from anyone. But if you have a Juan, then give them the time and space to do their thing and it will pay off.

Next, I will talk about what adopting TypeScript means in practice, it is often not an all-or-nothing move. There is a typical maturity model that I will try to define and analyse; starting from a not-strict mode, use of any, assertions and casts, tightening and refining types, recognising and documenting patterns and good practices, writing tests in TypeScript (or not) etc...

--

--