2 cases where Babel fixes your code but it shouldn't
How turning off transpilations messes up the codebase
Switching to babel-preset-env
Did you know that Babel inadvertently fixes some errors? Me neither. In one of my projects, I switched from
babel-preset-latest
to babel-preset-env
without expecting any problems.
But this is what happened. Since I don't care about legacy browsers, it immediately switched off all plugins and introduced two bugs in the codebase.
After some investigation, it turned out that some transpilations "fix" some edge cases. And in turn, switching them off surfaced the errors.
const
initialization
When you use block-scoped values, like a const
, Babel rewrites them to traditional var
s. It takes care
of block-scoping, so most of the times they behave as they should.
But it does not handle a specific scenario. For this input code (Try it):
const c = (() => {
return c;
})();
Babel outputs this:
"use strict";
var c = function () {
return c;
}();
It's almost identical; only the const
has been rewritten to var
. But if you run it, the babelified code sets
c
to undefined
, while the original one throws an Error.
The difference between const
and var
– apart from the scoping – is how they are hoisted. The var
declaration is moved to the top of the scope and set undefined
. Therefore, c
is accessible inside the
function call.
On the other hand, const
is moved to the top but left uninitialized. Inside the function call, c
is
still uninitialized, it is in the so-called temporal dead zone, so accessing it throws an Error.
Convert Array-like to iterable
The other edge case is how iterables are handled.
Consider the following code (Try it):
const o = {
length: 0
};
console.log([...o]);
o
is a so-called Array-like structure. It has a length
property, and the numerical keys return the appropriate
elements. In this case, it is an empty array (length: 0).
Running this code results in an error:
Uncaught TypeError: o is not iterable
The array destructuring operator works only on iterables. Array-likes are supported by some built-in functions, but they are not enough in this case.
Since iterables are required to have a [Symbol.iterator]
function and o
clearly does not have it,
the error is expected.
Babel produces the following code for the above input:
"use strict";
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var o = {
length: 0
};
console.log([].concat(_toConsumableArray(o)));
The key part is the _toConsumableArray
function. It makes a full-fledged Array out of its parameter. Since
Array.isArray(o)
is false
, it returns Array.from(o)
. That function accepts an Array-like and returns an Array.
Since then, it behaves exactly like []
. Therefore, the code returns an empty Array, instead of throwing an Error.
Conclusion
There might be other edge cases that get "fixed" by transpilation, and cases like these might surface errors in an update.
This might cause problems bumping even minor versions. For example, babel-preset-env
supports time-dependent queries,
like last x versions. With a new browser version, a plugin might get disabled, surfacing an error.
Fortunately, these errors are rare, and they are both known issues. But don't expect them to just go away. They are hard to implement right, and therefore, it's more likely you'll update sooner than the fix is released.
By knowing errors like these exist, give attention to plugins that get enabled/disabled during an update.