Skip to content

[@babel/plugin-proposal-decorators] static field with super reference incorrectly moved to wrapper class instance field (2023-11) #17929

@Lookwe69

Description

@Lookwe69

Bug Report

Description

When a class has a class decorator and a static field initializer that references super, @babel/plugin-proposal-decorators (version 2023-11) incorrectly moves the static field to an instance field in the _identity wrapper class. Because super in the wrapper class resolves to _identity (not the actual parent class), the initializer crashes at runtime.

Versions

  • @babel/core: 7.29.0
  • @babel/plugin-proposal-decorators: 7.29.0
  • version option: "2023-11"

Reproduction

const babel = require("@babel/core");

const code = `
  function customElement(tag) {
    return (cls, ctx) => { ctx.addInitializer(() => {}); };
  }

  class Base {
    static styles = ["base"];
  }

  @customElement("derived")
  class Derived extends Base {
    static styles = [...super.styles, "derived"];
  }

  console.log(Derived.styles);
`;

const result = babel.transform(code, {
  plugins: [["@babel/plugin-proposal-decorators", { version: "2023-11" }]],
});

eval(result.code);
// TypeError: (intermediate value).styles is not iterable

Generated output (simplified)

The plugin generates this pattern for the decorated class:

let _Derived;
new class extends _identity {
  static [class Derived extends Base {
    static {
      [_Derived, _initClass] = _applyDecs(this, [_dec], [], 0, void 0, Base).c;
    }
  }];
  styles = [...super.styles, "derived"]; // ❌ instance field, super = _identity
  constructor() {
    super(_Derived), _initClass();
  }
}();

The static field static styles = [...super.styles, "derived"] is moved to the wrapper class as an instance field styles = [...]. In this new context, super refers to _identity (a helper function), not Base. Since _identity.styles is undefined, the spread throws:

TypeError: (intermediate value).styles is not iterable

Expected behaviour

static styles = [...super.styles, "derived"] should remain a static field on Derived, with super.styles correctly resolving to Base.styles. Expected output:

["base", "derived"]

Workaround

Replacing super.styles with an explicit reference to the parent class avoids the issue:

@customElement("derived")
class Derived extends Base {
  static styles = [...Base.styles, "derived"]; // explicit reference works
}

Or using Object.getPrototypeOf(this).styles (since this in a static field initializer is the class itself).

Note: SWC (@swc/core with decoratorVersion: "2023-11") generates the identical wrapper class pattern and exhibits the same bug. Issue filed at swc-project/swc#11780.

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions