evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
38.18k stars 1.15k forks source link

When useDefineForClassFields is false and the class has a decorator, private fields are incorrectly initialized before the super call #3939

Open volkanceylan opened 1 month ago

volkanceylan commented 1 month ago

I think this is an issue introduced in 0.24 as 0.23.1 does not have this problem. Probably related to https://github.com/evanw/esbuild/issues/3913

{
  loader: 'ts',
  format: 'iife',
  target: 'es2015',
  tsconfigRaw: {
    compilerOptions: {
      useDefineForClassFields: false
    }
  }
}
function testDecorator(a: string) {
  return function (target: Function, _context?: any) { }
}

class A {
   constructor() {
   }
}

@testDecorator("a")
class B extends A {
  private m1: any;

  constructor() {
    super();
  }

}

new B();

The generated constructor for the derived B class with a private field includes a field initializer before the super call which results in the following error in browser console if you copy paste the generated code:

VM17:64 Uncaught ReferenceError: Must call super constructor in derived class 
before accessing 'this' or returning from derived constructor
    at new B (<anonymous>:64:7)
    at <anonymous>:71:3
    at <anonymous>:72:3
class B extends (_a = A) {
    constructor() {
      this.m1 = void 0;
      super();
    }
  }
volkanceylan commented 1 month ago

Note that this only occurs if the property has no assigned value. If private m1: any = null is used, the initializer is correctly generated after the super() call.

volkanceylan commented 1 month ago

Another thing to consider after looking at https://github.com/evanw/esbuild/issues/3913 is, if this behavior should apply only for properties with decorators or even when only the class has a decorator as in my case. Otherwise this will be a breaking change for those using useDefineForClassFields: false. Does TypeScript behave this way, e.g. also for class decorators?

volkanceylan commented 1 month ago

Seems like TypeScript does not do this for classes with only class decorators:

https://www.typescriptlang.org/play/?target=2#code/GYVwdgxgLglg9mABFApgZygERROAnAQynwAooC8BzFKALkQLAE8AaRXMVADzoeYEpEAbwBQAXxEiIAGwJo0iAILCRiNRwx4Q0UoNFrEEiSIACqDNlyFieKbPmIAQohQ8UYACYLl+xAFsARnpGJgBuSXUETW0bEj1VAzQQAAcUPDjwtWNjMBQAdycMoA

But only for properties with decorators:

https://www.typescriptlang.org/play/?target=2#code/GYVwdgxgLglg9mABFApgZygERROAnAQynwAooC8BzFKALkQLAE8AaRXMVADzoeYEpEAbwBQAXxEiIAGwJo0iAILCRiNRwx4Q0UoNFrEEiVNnzEAIUQoeKMABMFy-YgACqDNlyFieVYgAOeDAAbkQoiAC2AIz0jEwA3JLqCJraPiR6fmpoIP4oeBmJasbGYCgA7haFQA

Should ESBuild do the same?