Skip to content

Commit

Permalink
Add support for "private" custom elements (#1943)
Browse files Browse the repository at this point in the history
In typescript, users can specify a private constructor to prevent
subclassing

```ts
class XFoo extends HTMLElement {
  private constructor() {
    super();
  }
}
```

In order to support this construction with `@customElement`, we have to
switch from using `Constructor<HTMLElement>` to `Omit<typeof
  HTMLElement, 'new'>`.
  • Loading branch information
dfreedm committed Jun 9, 2021
1 parent 660192d commit 39ad574
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 7 deletions.
16 changes: 9 additions & 7 deletions packages/reactive-element/src/decorators/custom-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
*/
import {Constructor, ClassDescriptor} from './base.js';

const legacyCustomElement = (
tagName: string,
clazz: Constructor<HTMLElement>
) => {
window.customElements.define(tagName, clazz);
/**
* Allow for custom element classes with private constructors
*/
type CustomElementClass = Omit<typeof HTMLElement, 'new'>;

const legacyCustomElement = (tagName: string, clazz: CustomElementClass) => {
window.customElements.define(tagName, clazz as CustomElementConstructor);
// Cast as any because TS doesn't recognize the return type as being a
// subtype of the decorated class when clazz is typed as
// `Constructor<HTMLElement>` for some reason.
Expand Down Expand Up @@ -57,7 +59,7 @@ const standardCustomElement = (
*/
export const customElement =
(tagName: string) =>
(classOrDescriptor: Constructor<HTMLElement> | ClassDescriptor) =>
(classOrDescriptor: CustomElementClass | ClassDescriptor) =>
typeof classOrDescriptor === 'function'
? legacyCustomElement(tagName, classOrDescriptor)
: standardCustomElement(tagName, classOrDescriptor);
: standardCustomElement(tagName, classOrDescriptor as ClassDescriptor);
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,15 @@ suite('@customElement', () => {
const DefinedC = customElements.get(tagName);
assert.strictEqual(DefinedC, C0);
});
test('elements with private constructors can be defined', () => {
const tagName = generateElementName();
@customElement(tagName)
class C1 extends HTMLElement {
private constructor() {
super();
}
}
const DefinedC = customElements.get(tagName);
assert.strictEqual(DefinedC, C1);
});
});

0 comments on commit 39ad574

Please sign in to comment.