Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decorators cause output to differ from TSC #2629

Closed
simon-abbott opened this issue Oct 24, 2022 · 3 comments · Fixed by #3167
Closed

Decorators cause output to differ from TSC #2629

simon-abbott opened this issue Oct 24, 2022 · 3 comments · Fixed by #3167
Labels

Comments

@simon-abbott
Copy link

When using esbuild with target: "ES2022", having decorators in classes causes the output to differ from tsc in a way that breaks self-referential static members.

Input file

// Stub out the decorator so TSC doesn't complain.
const someDecorator = (): PropertyDecorator => () => {};

class Foo {
  static message = 'Hello world!';
  static msgLength = Foo.message.length;

  @someDecorator()
  foo() {}
}

export { Foo };

tsc output

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
// Stub out the decorator so TSC doesn't complain.
const someDecorator = () => () => { };
class Foo {
    static message = 'Hello world!';
    static msgLength = Foo.message.length;
    foo() { }
}
__decorate([
    someDecorator()
], Foo.prototype, "foo", null);
export { Foo };

esbuild output

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};
const someDecorator = () => () => {
};
const _Foo = class {
  static message = "Hello world!";
  static msgLength = _Foo.message.length;
  foo() {
  }
};
let Foo = _Foo;
__decorateClass([
  someDecorator()
], Foo.prototype, "foo", 1);
export { Foo };

Running the esbuild version crashes with the error ReferenceError: Cannot access '_Foo' before initialization, pointing to the line static msgLength = _Foo.message.length;

This only happens when the target is ES2022 (or later) AND the the class has at least one decorator. If either of those conditions is not met, then the resulting code is fine.

@rossng
Copy link

rossng commented Oct 31, 2022

I'm also experiencing this problem (in my case, when running unit tests in Vitest).

If for whatever reason it's not possible to do what tsc does, I suppose the anonymous class could also be given a name, like so:

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};
const someDecorator = () => () => {
};
const _Foo = class __Foo {
  static message = "Hello world!";
  static msgLength = __Foo.message.length;
  foo() {
  }
};
let Foo = _Foo;
__decorateClass([
  someDecorator()
], Foo.prototype, "foo", 1);
export { Foo };

@wesselvdv
Copy link

Same here, anything less than ES2022 fixes it. In my case Angular Dependency Injection was failing due to missing injection tokens because of this.

@evanw
Copy link
Owner

evanw commented Jun 15, 2023

Note to self: playground link for this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants