Class binding references in static field initializers should be resolved to the decorated class
Input (playground):
class D {}
@(() => D)
class C {
static p = C
}
console.log(C.p === D)
Option:
{ target: "es2022" }
Expected: It should print true, as per spec, static field initializers (step 40) should run after the class binding was initialized (step 38).
Actual: It prints false.
I think it's unclear what should happen here until https://github.com/tc39/proposal-decorators/issues/529 is resolved, as the specification may be changed. Supposedly that may be resolved after the upcoming TC39 meeting (from June 11th to June 13th?).
If I read correctly, the discussion result in https://github.com/tc39/proposal-decorators/issues/529 is that the homeObject of the static accessor might be reverted back to this. But the decision that class decorators should run before static fields was made long ago (c.f. https://github.com/tc39/proposal-decorators/issues/329#issuecomment-695365599). So the outcome of https://github.com/tc39/proposal-decorators/issues/529 should not affect the behaviour mentioned in this issue.
In that case, consider the following test case:
let old
let block
class Bar {}
@(cls => (old = cls, Bar)) class Foo {
static { block = Foo }
method() { return Foo }
static method() { return Foo }
field = Foo
static field = Foo
get getter() { return Foo }
static get getter() { return Foo }
set setter(x) { x.foo = Foo }
static set setter(x) { x.foo = Foo }
accessor accessor = Foo
static accessor accessor = Foo
}
let foo = new old
let obj
console.log(
Foo !== old,
Foo === Bar,
block === Bar,
Foo.field === Bar,
old.getter === Bar,
(obj = { foo: null }, old.setter = obj, obj.foo) === Bar,
foo.field === Bar,
foo.getter === Bar,
(obj = { foo: null }, foo.setter = obj, obj.foo) === Bar,
// These are determined by the outcome of https://github.com/tc39/proposal-decorators/issues/529
// Foo.accessor === Bar,
// old.accessor === Bar,
)
I think you're saying they should all return true here. That's currently only the case in Babel, not in esbuild or in TypeScript. I need to fix esbuild's handling of this.
I believe how accessors behave in this scenario is still determined by the outcome of https://github.com/tc39/proposal-decorators/issues/529. Currently old.accessor is specified to throw and Foo.accessor is specified to be undefined, although that may change so it's not really something we can write a test for at the moment. But I think what you're saying is whatever the behavior of accessors ends up being, there will be some way to get the value (whether it's old.accessor or Object.getOwnPropertyDescriptor(old, 'accessor').get.call(Foo)) and that value should be the right one.