Skip to main content

7.14.0 Released: New class features enabled by default, TypeScript 4.3, and better CommonJS interop

· 5 min read

Babel 7.14.0 is out!

This release enables class fields and private methods by default (they were promoted to Stage 4 during the recent April TC39 meeting!) and adds brand checks for private fields and static class blocks to @babel/preset-env's shippedProposals option.

We added support for Stage 1 async do expressions (using @babel/plugin-proposal-async-do-expressions), which extends the Stage 1 do expression proposal.

Thanks to Sosuke Suzuki and Pig Fang, Babel can now handle TypeScript 4.3 features. @babel/parser also has a new option to correctly parse TypeScript declaration files.

Finally, we introduced a new importInterop: node option to make it easier to produce dual modules by compiling ECMAScript imports to CommonJS that follow Node.js semantics.

You can read the whole changelog on GitHub.

If you or your company want to support Babel and the evolution of JavaScript, but aren't sure how, you can donate to us on our Open Collective and, better yet, work with us on the implementation of new ECMAScript proposals directly! As a volunteer-driven project, we rely on the community's support to fund our efforts in supporting the wide range of JavaScript users. Reach out at if you'd like to discuss more!


New class features enabled by default

The class fields and private methods proposals just reached Stage 4 and will be officially included in ECMAScript 2022! This was more of a formality since the semantics were already finalized and they've already been implemented in all the major browsers.

You can read more details about this new syntax on MDN (public fields, private fields and methods).

class Check {
static className = "Check"; // static public class field

#value = 3; // # means private!

get #double() { // private getter
return this.#value * 2; // using a private field

Thus, you can remove @babel/plugin-proposal-class-properties and @babel/plugin-proposal-private-methods, since they are now enabled by default in @babel/preset-env.


Webpack supports this syntax natively as of v5.36.0. For older versions, a workaround that works with simpler Webpack setups is to manually enable the acorn-stage3 plugin, by installing acorn-stage3 and adding these lines at the beginning of your webpack.config.js file:

// Require webpack's acorn dependency
const acorn = require(require.resolve("acorn", {
paths: [require.resolve("webpack")]

// Enable the Stage 3 plugin
acorn.Parser = acorn.Parser.extend(require("acorn-stage3"));

If this doesn't work for you, or if you use a different tool that doesn't support class fields, you still need to use the Babel plugins to transform them.

If you are using @babel/preset-env's shippedProposals option, it now also includes the @babel/plugin-proposal-private-property-in-object (introduced in 7.10) and @babel/plugin-proposal-class-static-block (introduced in 7.12) plugins: you can safely remove them from your configuration.

class Foo {
#bar = "bar";

test(obj) {
return #bar in obj; // private-property-in-object

static #x = 42;
static y;
static { // static block
try {
this.y = doSomethingWith(this.#x);
} catch {
this.y = "unknown";

Better ESM-CJS interop

When importing a CommonJS file from an ECMAScript module, Node.js has different semantics than most of the tools in the JavaScript ecosystem.

Suppose that you are depending on the following library:

export default function two() {
return 2;

And the author of this library doesn't publish it as-is, but compiles it to CommonJS:

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = two;

function two() {
return 2;

When importing this library with Babel (or TypeScript, Rollup or similar tools) and compiling your code to CommonJS, it will look like:

import two from "two";

One day, you decide to provide two versions of your code: a compiled CommonJS one, and one using native ECMAScript modules.

While the compiled version works, the ESM one will throw TypeError: two is not a function. This is because in Node.js, the default import is not the dependency's exports.default, but the whole module.exports object instead.

You could change your code to:

import two from "two";

However, this new code has a problem: it now doesn't work when compiled, because two.default is not a function.

Babel v7.14.0 adds a new importInterop: "node" option in the @babel/plugin-transform-modules-commonjs plugin that allows import statements to match the native Node.js behavior. You can read more about this option in the docs.

Nicolò from our team also contributed a similar option to @rollup/plugin-commonjs, which will be out in the next release. Our goal is to help the ecosystem migrate to native ECMAScript modules by providing an easier migration path.

TypeScript 4.3

The new TypeScript version, which will be released as stable in May, supports a few new features:

  • override modifiers in class elements
  • static index signatures ([key: KeyType]: ValueType) in classes
  • get/set in type declarations

You can read more about them in the TypeScript 4.3 release post. This is supported through @babel/preset-typescript.

async do expressions

async do expressions are a Stage 1 proposal built on top of the do expressions proposal.

They allow using asynchronous blocks within synchronous code, and those blocks are evaluated as a promise:

function sync() {
let x = async do {
let res = await Promise.resolve("Third!");

// Logs:
// - "First!"
// - "Second!"
// - "Third!"

You can test this proposal (and report feedback!) by adding the @babel/plugin-proposal-do-expressions and @babel/plugin-proposal-async-do-expressions plugins to your Babel configuration.


These proposals are highly experimental. They can, and likely will continue to evolve. It could take years before they are standardized, and may even be rejected altogether. You are welcome to test them, but we do not recommend using them in production.

Do you have any comment or question? Discuss on GitHub!