Re: [WICG/webcomponents] A better solution for ElementInternals (Issue #1036)

Oops, I realized that with `new SomeElement` there's actually no way for the browser to call an internals callback like it could with `document.createElement('some-element')` or during parsing, unless we wait for decorators.


I'm guessing this is why the internals callback idea was not viable (decorators were not even stage 3 at the time).

Only a class decorator can ensure that the internals callback is called during construction with `new` _after the user constructor so that private fields are ready_.

The method decorator idea won't work: it can add an initializer in which could call the internals callback during the user's constructor but the initializer runs _before_ fields are initialized, which means users will not be able to use private fields in the callback because they'll get a runtime error:

```ts
function methodDeco(value: unknown, context: ClassMethodDecoratorContext) {
  context.addInitializer(function(this: any) {
    this[context.name]({foo: 123})
  })
}

class MyClass {
  #foo: any

  @methodDeco receiveInternals(obj: {foo: number}) {
    this.#foo = obj
  }

  logInternals() {console.log(this.#foo)}
}

new MyClass().logInternals() // TypeError: Cannot write private member to an object whose class did not declare it
```

[TS playground](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAWwKZQBZwCYBFURwAUAbgIYA2IqAXIuANZhwDuYANIoWFKgB5Q6AYQpkAzmICy6LHgJwATmSiKhCXgICUiAN4AoRF3X8oAOjLZsASTAxYlGAC9UCoqEiwERTDDF0yYACe2vqGhj5iANrcGmZgZGgAukQ6wHBwdACMAEwAzAC+mgaIhXr5enoQohKIkoEi4mK6xQDEaRmIAYEVhgACaJg4+ISICgSoMCSoNrwK8RRiRHAARgBWdKnpdGAgyMsuhc1hiBGmbemIALyIK6vF5cUUcADmMy7ziyHcYnAUqKZPZ7eDC+M7tTTlB5gVAsWr1aqfAEvN5zSifIA) (hit "Run" to see the error)

A class decorator would work like so:

```ts
function classDeco<T extends new (...args: any[]) => any>(value: T, context: ClassDecoratorContext) {
  return class extends value {
    constructor(...args: any[]) {
      super(...args)
      this.internalsCallback({foo: 123})
    }
  }
}

@classDeco
class MyClass {
  #foo: any

  internalsCallback(obj: {foo: number}) {
    this.#foo = obj
  }

  logInternals() {console.log(this.#foo)}
}

new MyClass().logInternals()
```

[TS playground](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABBANgQwM4YCIFMJwA8AKorgB5S5gAmGiYuA7ogBQB0naATgOYYAuRGjABPANoBdAJSIAvAD5hYhawBuaFCFxDiAGmQIqlIQGF0WPAW5oocbqaMUosgN4AoRIm64oIbkiomPTO1HSIGlq4iB5eXgRgGFDcIND2HFx8gsoSMjGecV4YIAAOuNwZ7Dz80gWFUAAWMBjsMGBUAZoYppooAEZoEADWrK7AcHBCAIwATADMAL61hQsFq6vuAAJBlvhw7jv0ALKi5sH5XgDE45M57gVtHWBdPSj9gyNwfQBWQmMTQjAIAAtn1yksLnFGs12NcJvJEF9vmt7l4UHBeABJdrlZ4oDCsNwJDBwFC4djo3isaEtOFwaTre6MFgnM5YQkUjHYp5dQlAA) (click "Run" and see console output)

-- 
Reply to this email directly or view it on GitHub:
https://github.com/WICG/webcomponents/issues/1036#issuecomment-1814102675
You are receiving this because you are subscribed to this thread.

Message ID: <WICG/webcomponents/issues/1036/1814102675@github.com>

Received on Thursday, 16 November 2023 09:45:19 UTC