Compiler assumptions

By default Babel tries to compile your code so that it matches the native behavior as closely as possible. However, this sometimes means generating more output code, or slower output code, just to support some edge cases you don’t care about.

Since Babel 7.13.0, you can specify an assumptions option in your configuration to tell Babel which assumptions it can make about your code, to better optimize the compilation result. Note: this replaces the various loose options in plugins in favor of top-level options that can apply to multiple plugins (RFC link).

For example:

babel.config.json

  1. {
  2. "targets": ">0.5%",
  3. "assumptions": {
  4. "noDocumentAll": true,
  5. "noClassCalls": true
  6. },
  7. "presets": ["@babel/preset-env"]
  8. }

Compiler assumptions - 图1caution

This is advanced functionality. Please be careful when enabling assumptions, because they are not spec-compliant and may break your code in unexpected ways.

Compiler assumptions - 图2tip

Are you migrating from @babel/preset-env‘s loose and spec options to granular assumptions? Check “Migrating from @babel/preset-env’s “loose” and “spec” modes” for the equivalent assumptions-based configuration, ready to be copied and pasted as a starting point.

arrayLikeIsIterable

When spreading or iterating an array-like object, assume that it implements a [Symbol.iterator] method with the same behavior of the native Array.prototype[Symbol.iterator], and thus directly iterate over its element by index.

This can be useful, for example, to iterate DOM collections in older browsers.

JavaScript

  1. let images = $("img");
  2. for (const img of images) {
  3. console.log(img);
  4. }
  5. const copy = [...images];

constantReexports

When re-exporting a binding from a module, assume that it doesn’t change and thus it’s safe to directly export it, as if you were doing

JavaScript

  1. import { value as val } from "dep";
  2. export const value = val;

NOTE: This also affects the transform-modules-umd and transform-modules-amd plugins.

JavaScript

  1. export { value } from "dependency";

constantSuper

The super class of a class can be changed at any time by using Object.setPrototypeOf, making it impossible for Babel to statically know it. When this option is enabled, Babel assumes that it’s never changed and thus it is always the value that was placed in the extends clause in the class declaration.

JavaScript

  1. class Child extends Base {
  2. method() {
  3. super.method(2);
  4. }
  5. }

enumerableModuleMeta

When compiling ESM to CJS, Babel defines a __esModule property on the module.exports object. Assume that you never iterate over the keys of module.exports or of require("your-module") using for..in or Object.keys, and thus it’s safe to define __esModule as enumerable.

JavaScript

  1. export const number = 2;

ignoreFunctionLength

Functions have a .length property that reflect the number of parameters up to the last non-default parameter. When this option is enabled, assume that the compiled code does not rely on this .length property.

JavaScript

  1. function fn(a, b = 2, c, d = 3) {
  2. return a + b + c + d;
  3. }

ignoreToPrimitiveHint

When using language features that might call the [Symbol.toPrimitive] method of objects, assume that they don’t change their behavior based on the hint parameter.

JavaScript

  1. let str = `a${foo}b`;

iterableIsArray

When using an iterable object (in array destructuring, for-of or spreads), assume that it is an array.

JavaScript

  1. const [first, ...rest] = obj;
  2. call(first, ...obj);
  3. let arr = [first, ...obj];
  4. for (const el of obj) {
  5. console.log(el);
  6. }

mutableTemplateObject

Don’t use Object.freeze for the template object created for tagged template literals. This effectively means using the taggedTemplateLiteralLoose helper instead of taggedTemplateLiteral.

JavaScript

  1. let str = tag`a`;

noClassCalls

When transforming classes, assume that they are always instantiate with new and they are never called as functions.

JavaScript

  1. class Test {
  2. constructor() {
  3. this.x = 2;
  4. }
  5. }

noDocumentAll

When using operators that check for null or undefined, assume that they are never used with the special value document.all.

JavaScript

  1. let score = points ?? 0;
  2. let name = user?.name;

noIncompleteNsImportDetection

Assume that no own property of a module export object is observed before initialization. For example, when trying to access ns.foo, it will return undefined both with this assumption turned on or off. The difference is that Object.prototype.hasOwnProperty.call(ns, "foo") would return false when noIncompleteNsImportDetection: true.

JavaScript

  1. export var foo;

noNewArrows

Assume that the code never tries to instantiate arrow functions using new, which is disallowed according to the specification.

NOTE: This assumption defaults to true. It will default to false starting from Babel 8.

JavaScript

  1. let getSum = (a, b) => {
  2. return { sum: a + b }
  3. };

noUninitializedPrivateFieldAccess

History

VersionChanges
v7.24.0Added noUninitializedPrivateFieldAccess assumption

Assume that code never attempts to access private fields on classes before they are initialized. For example:

JavaScript

  1. class Foo {
  2. x = this.#y; // #y is not initialized yet
  3. #y = 2;
  4. }

JavaScript

  1. class MyClass {
  2. static #id = 123;
  3. method() {
  4. return MyClass.#id;
  5. }
  6. }

objectRestNoSymbols

When using rest patterns in object destructuring, assume that destructured objects don’t have symbol keys or that it’s not a problem if they are not copied.

JavaScript

  1. let { name, ...attrs } = obj;

privateFieldsAsProperties

Assume that “soft privacy” is enough for private fields, and thus they can be stored as public non-enumerable properties with an unique name (rather than using an external WeakMap). This makes debugging compiled private fields easier.

JavaScript

  1. class Foo {
  2. #method() {}
  3. #field = 2;
  4. run() {
  5. this.#method();
  6. this.#field++;
  7. }
  8. }

Compiler assumptions - 图3caution

When using inline Babel helpers, generated string keys are unique per-file and not globally. This could cause conflicts when extending classes from a different fields with private fields with the same name.

privateFieldsAsSymbols

History

VersionChanges
v7.21.0Added privateFieldsAsSymbols assumption

Assume that “soft privacy” is enough for private fields, and thus they can be stored as public properties with a symbol key (rather than using an external WeakMap). This makes debugging compiled private fields easier.

  1. class Foo {
  2. #method() {}
  3. #field = 2;
  4. run() {
  5. this.#method();
  6. this.#field++;
  7. }
  8. }

pureGetters

Assume that getters, if present, don’t have side-effects and can be accessed multiple times.

JavaScript

  1. let a = obj;
  2. a.b?.();

setClassMethods

When declaring classes, assume that methods don’t shadow accessors or non-writable properties on the superclass prototype, and that the program doesn’t depend on methods being non-enumerable. Thus, it’s safe to assign methods rather than using Object.defineProperty.

JavaScript

  1. class Foo extends Bar {
  2. method() {}
  3. static check() {}
  4. }

setComputedProperties

When using computed object properties, assume that the object doesn’t contain properties that overwrite setter defined in the same object, and thus it’s safe to assign them rather than defining them using Object.defineProperty.

JavaScript

  1. let obj = {
  2. set name(value) {},
  3. [key]: val
  4. }

setPublicClassFields

When using public class fields, assume that they don’t shadow any getter in the current class, in its subclasses or in its superclass. Thus, it’s safe to assign them rather than using Object.defineProperty.

JavaScript

  1. class Test {
  2. field = 2;
  3. static staticField = 3;
  4. }

setSpreadProperties

When using object spread, assume that spreaded properties don’t trigger getters on the target object and thus it’s safe to assign them rather than defining them using Object.defineProperty.

JavaScript

  1. const result = {
  2. set name(value) {},
  3. ...obj,
  4. };

skipForOfIteratorClosing

When using for-of with an iterator, it should always be closed with .return() and with .throw() in case of an error. When this option is called Babel assumes that those methods are not defined or empty, and it avoids calling them.

JavaScript

  1. for (const val of iterable) {
  2. console.log(val);
  3. }

superIsCallableConstructor

When extending classes, assume that the super class is callable. This means that it won’t be possible to extend native classes or built-ins, but only compiled classes or ES5 function constructors.

JavaScript

  1. class Child extends Parent {
  2. constructor() {
  3. super(42);
  4. }
  5. }

Migrating from @babel/preset-env‘s "loose" and "spec" modes

@babel/preset-env‘s loose option is equivalent to the following configuration:

JSON

  1. {
  2. "presets": [
  3. ["@babel/preset-env", { "exclude": ["transform-typeof-symbol"] }]
  4. ],
  5. "assumptions": {
  6. "arrayLikeIsIterable": true,
  7. "constantReexports": true,
  8. "ignoreFunctionLength": true,
  9. "ignoreToPrimitiveHint": true,
  10. "mutableTemplateObject": true,
  11. "noClassCalls": true,
  12. "noDocumentAll": true,
  13. "objectRestNoSymbols": true,
  14. "privateFieldsAsProperties": true,
  15. "pureGetters": true,
  16. "setClassMethods": true,
  17. "setComputedProperties": true,
  18. "setPublicClassFields": true,
  19. "setSpreadProperties": true,
  20. "skipForOfIteratorClosing": true,
  21. "superIsCallableConstructor": true
  22. }
  23. }

@babel/preset-env‘s spec option is equivalent to the following configuration:

JSON

  1. {
  2. "presets": ["@babel/preset-env"],
  3. "assumptions": {
  4. "noNewArrows": false,
  5. }
  6. }