JS is not worse than …

Andrea Giammarchi
5 min readJun 14, 2018

Let me start with the reason I’m quickly writing this post, by mentioning a great comment from Jason that I’ve recently read on twitter:

Beside developers of the “moaners gonna moan” category, and excluding WASM as generic target, it seems to me few Web developers keep forgetting whatever language they choose will inevitably end up transpiled as JS, bringing a whole new world of issues they might not even be aware of, issues that, however, could hit hard once deployed in production.

Most of them also believe their code is better, safer, or even faster, ignoring all those nobel sentiments live only on their own development environment, rarely reflected in the production code they ship to users.

CoffeeScript

As must to be mentioned revolutionary way to develop with a differentish programming language, CoffeeScript always had great ideas and dev friendly shortcuts, most of them inspiring for modern JS.

Nowadays though, it looks indeed like modern JS, except since everything is an expression, but with no const neither let declaration, there’s basically no way to guard anything in a scope, classes included.

# all valid
same = () -> true;
same = () -> false;
# still valid
class Wut
class Wut
# will it be scoped ?
test = () -> same = () -> null
o = Wut
console.log(
test(o)(o) # null
);

On top of that, super has limited powers, compared to JS, and there is no sugar to define getters or setters, which is IMO™ an unnecessary limitation.

As much as it’s been a game changer at its time, and a honest hat tip for that, if I were you, I’d consider leaving this variant of modern JS, simply because it’s not really revolutionary anymore.

TypeScript

TypeScript, TS for short, is somehow the most popular choice between those developers liking types, but it’s also since ever quite problematic with that.

It’s not by design that TS has problems, it’s due its transpiled output logic.

TypeScript fails indeed most basic classes tests when you transpile those for backward compatibility:

class List extends Array {
method() {}
}
console.log(new List instanceof List); // false(new List).method();// TypeError: (intermediate value).method is not a function
// ^ .......: oh, the irony!

Yeah but …” … let me stop you there, in case it’s not clear: extendings builtins doesn’t breaks only with Array, it breaks with every builtin class, including DOM related one, hence “bye bye” Web Components and Custom Elements:

class MyElement extends HTMLElement {
connectedCallback() {
this.textContent = new Date;
}
}
customElements.define('my-element', MyElement);
document.body.appendChild(new MyElement);

Try above basic code after transpiling it via TypeScript, and see errors that are likely not getting fixed any time soon.

And that, my fellow developers, is where your type safety go: you have now more hidden issues and limits than those you think types would solve for you.

But as remind note: there are no types in your production code, only transpiled code your IDE cannot spot, code that will fail where, and when, it matters the most.

I wonder what’s stopping TS lovers to use flow instead, ’cause if types are really all they are after, flow is at least just on top of JS, and you can blame only JS if things don’t work as expected, and not its unknown alter ego.

Babel

Not really a language a part, but Babel is the awesome tool responsible for transpiling most of the modern code.

Bad news is, if you don’t have tests that run through the production code too, you can ignore any “green build badge”, because that’s not really what runs through your users.

TL;DR you need to test live prod code too, possibly on every target browser.

As a matter of fact, pretty much everything failing on TypeScript, will equally fail on Babel 6, unless you are aware of plugins that solve classes issuse, or others gotchas, ’cause ES6, ES7, and ES8, are notjust sugar” for ES5.

And while Babel 7 fixed at least classes extends “shenanigans”, the produced code might still not be 100% reliable so … keep testing production code too!

Another tiny point in favor of not abusing transpilers, is the final code size.

Take the following native JS, as example:

const wut = (...wait) => wait.join('');

that will become:

'use strict';
var wut = function wut() {
for (var _len = arguments.length, wait = Array(_len), _key = 0; _key < _len; _key++) {
wait[_key] = arguments[_key];
}
return wait.join('');
};

instead of:

function wut() { return [].join.call(arguments, ''); }

And you can benchmark those variants and realize results are basically the same, except the size of the dev code, versus the machine one, weights 49 bytes, once minified, instead of 106 produced by the Babel one.

That hits every time you spread arguments and transpile; just saying …

Elm

I’ve heard a lot of great things about Elm, and while I’ve promised myself I would’ve digged it more, I’ve often been pushed back by checking its issues.

The language apparently generates JS with great performance and no runtime exceptions, but its most recent issues are like:

  • Should (but does not) error if two modules define the same port
  • Compiler hangs on infinite type in badly written foldl lambda
  • Impossible record extension inference causes out of memory crash
  • Errors due to lack of kind checking
  • infinite type causes compiler hang
  • crash when unannotated definition has type variable in sub-annotation
  • Infinite type error on type that does not appear to be infinite 🤯

As much as I admire such language has actually few opened issues, my brain exploded only at reading the last issue there, and I honestly don’t feel (yet?) too ready to explore, embrace, or understand, any of those bugs.

Cuddled by native JS engines that also infer types, but simply de-opt when things don’t go as expected ( instead of “exploding” in my face 😅 ), I think for now I’ll skip Elm, but I’m curious to know how much it solved for you VS JS.

And did I mention already I love the effort engieers behind JS runtimes put to make JS one of the fastest scripting languages out there, while preserving what makes it great for everyone since ever, which is its ease of use?

Dart

At version 2, it still doesn’t support template literals …

Update: it actually does, in its own way.

Rust

OK, OK, we’re heading to languages that target more WASM than JS, but since this post is about developers thinking that JS is always worse than others, I’d like to leave this little tweet here while flying away 👋

--

--

Andrea Giammarchi

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