TypeScript: when it helps & when it’s worse!

Andrea Giammarchi
4 min read3 days ago

--

I love TS until the point I hate it and this post is about the latter part.

Community Expectations

Well … Yes! Like many other core libraries’ developers that would avoid TS for reasons I’ll explain in here, among others, we all want to publish on npm something that can provide a great DX all over the developers’ space: targeting people sticking with vanilla JS or people using TS as default, and it’s all doable behind the scene!

This post is about exploiting just one of (hopefully not) many things TS makes you believe your code is better, or safer, while it’s not, actually the opposite … and from a developer (me) that, during his certified PHP days was all about “strict” (or even all) error reporting around the code, trust me: this is not looking good at all, especially when TS enthusiasts don’t understand the underlying issue!

A Failing Use Case

You can try yourself this simple case over TypeScript Playground:

const wm = new WeakMap<Object,string[]>;
const key = {};

if (!wm.has(key))
wm.set(key, []);
wm.get(key).push('WUT?');
// ^^^^^^^^ oh, "the horror"!

You can play with strict VS non strict behavior and see that the error will always be the same:

Object is possibly 'undefined'.

And What Could Possibly Go Wrong …

First of all, from a single-threaded PL like JS is, nothing at all could possibly go wrong … unless:

  • the guard is about using a potentially poisoned get method of a Map or a WeakMap randomly used in the code (spoiler: it’s not!)
  • the guard is about TS not understanding the context around that WeakMap or Map in the wild … 🤔

Not sure you really needed to think about these options, but the answer is … 🥁🥁🥁 … the latter! 🥳

If a .get can fail, so can any .has or .set accordingly, but most importantly, in there I wrote code I want to be sure about that if such .get fails, I expect a “poisoned environment, GTFO or bad things will happen!” sort of instantly throwing message … right? … right?

I don’t want that code to silently fail and moveover because I trust I can find what I have been looking for (literally 2 LOC before, but the logic breaks further) … and I would never expect any error otherwise, accordingly to the logic I explicitly wrote!

Enter TS Misleading Safety Feeling

Most of the TS lovers still reading this, would just fix that issue with an “innocent?, isn’t it?

// previous code ...
wm.get(key)?.push('WUT?');

… fair enough, now I am going to ask you: what did that change to your original intent in writing the code the way you meant to write it?

Here some clues:

  • that code will silently fail in there, keep going and doing things after instead of throwing in case that .get method was poisoned by evil code around (that is WeakMap or Map .prototype.get to be clear)
  • my code trusts no evil code is around (bad for libraries authors, still the most common case in the wild) so I just want the TS compiler to be happy … writing meaningless and potentially less predicable code in the making … (congrats? when that will become a habit? 🙃)

Again, fair enough if you are OK with the second clue, yet … have you read, and understood, the first one at all?

Basically what we are saying is:

it’s OK if TS makes me write code that is more prone to 3rd party evil attacks and bugs, as I cannot guarantee anymore my expectations would fail fast when needed, i.e. when a poisoned or broken .get happens!

… but wasn’t the whole premises of TS about writing better code?

Enter @ts-ignore

The moment I need to write that comment anywhere in my otherwise fully valid JS code, is the moment I start questioning what TS really brings on the table … to me it starts falling into one of these categories:

  • I use TS to help me refactoring later on (likely the only use case I agree about)
  • I use TS because it makes me write better code (honestly the worst use case I agree about)
  • I use TS because everyone else uses it (fair, in terms of job-market thinking, yet … not fully satisfying?)

Wondering about me? … Well, I am more about:

  • I use TS because the community asks for it and my IDE understands JSDoc TS so that occasionally I have hints instead of shenanigans created by TS itself I need to fix later

… and that’s pretty much it, yet happy when it works!

Personal Conclusions

TS is there to stay, or even take over the JS world, but it has still tons of things to improve and I feel like it’s still in its early stages, despite all the great things it improved over recent years.

My personal thinking though, is why anyone would chose to make a powerful scripting language less powerful, and more error prone, due intrinsic conflicts between being strictly typed and naturally being “just a scripting language” (if not the best one) like JS is … I feel like everyone claims successful stories around how better TS made their daily workflow but nobody is able to admit how many times that workflow is stubbornly stuck behind, or slower due, TS incapability to really understand the JS code expectation and logic underneath, like I’ve shown in this post.

Once again, I love TS and what it does for me on daily basis, when I try to make it work out of JSDoc TS awesome integration, but it is in these cases that I wonder if I’m wasting my time instead, using directly or inderectly a project that feels not fully there yet.

Curious to know about your feelings around this topic too though and please, no hate, just sharing, thanks 👋

Closing with this gem: folks, it’s not about how strict or not strict TS is, it’s about TS not understanding the code where strict checks make it just more obvious TS does not, in fact, understand the surrounding code!

--

--

Andrea Giammarchi
Andrea Giammarchi

Written by Andrea Giammarchi

Web, Mobile, IoT, and all JS things since 00's. Formerly JS engineer at @nokia, @facebook, @twitter.

Responses (2)