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

LitElement hard to use with mixins in TypeScript because it's an abstract class #227

Closed
sorvell opened this issue Sep 27, 2018 · 5 comments
Assignees

Comments

@sorvell
Copy link
Member

sorvell commented Sep 27, 2018

Because LitElement is an abstract class, it's hard to use with a mixin function that generates a class expression. Related issue: microsoft/TypeScript#5843.

To workaround this issue, perhaps LitElement should just not be an abstract class.

@sorvell sorvell self-assigned this Sep 27, 2018
@asyncLiz
Copy link

asyncLiz commented Oct 3, 2018

If anyone is interested, this is the workaround I currently use:

import { LitElement as AbstractLitElement } from '@polymer/lit-element';

// Makes LitElement non-abstract so that it can be used in mixins
// @ts-ignore
export class LitElement extends AbstractLitElement {}

I then import and extend this custom LitElement. It works in mixins, still requires render() to be implemented, and retains type safety.

Only downside is the extra code that's generated, but it's minimal enough to me to warrant its usage.

@aadamsx
Copy link

aadamsx commented Oct 3, 2018

@asyncLiz will you make a codepen or jsbin or whatever example?

@asyncLiz
Copy link

asyncLiz commented Oct 3, 2018

I'm not sure how to add an online example since you'd need the TS compiler, but here's a full example you can copy/paste.

import { LitElement, html } from '@polymer/lit-element';

// @ts-ignore
class FixedLitElement extends LitElement {}

type Constructor<T> = { new (...args: any[]): T };

function MyMixin<B extends Constructor<any>>(base: B) {
  return class extends base {
    isMixin = true;
  };
}

// class MyElement extends MyMixin(LitElement) { // "Cannot assign an abstract constructor type to a non-abstract constructor type."
class MyElement extends MyMixin(FixedLitElement) {
  constructor() {
    super();
    console.log(this.isMixin); // true from MyMixin
    console.log(this.updated); // function from UpdatingElement
  }

  render() { // Will not compile if render() is not implemented
    return html``;
  }
}

@hastebrot
Copy link

hastebrot commented Oct 3, 2018

Thanks @asyncLiz for the example code. I've extended your example to use decorators and a constructor in the mixin.

import { LitElement, customElement, property, html, Constructor as _Constructor } from "@polymer/lit-element"

// @ts-ignore
export class BaseElement extends LitElement {}

declare type Constructor<T> = {
  new (...args: any[]): T
}

export function LoggerMixin<B extends Constructor<HTMLElement>>(baseClass: B) {
  return class extends baseClass {
    _log: (...message: any[]) => void
    _info: (...message: any[]) => void
    _debug: (...message: any[]) => void
    _trace: (...message: any[]) => void
    _warn: (...message: any[]) => void
    _error: (...message: any[]) => void

    constructor(...args: any[]) {
      super(...args)

      const message = `[${this.localName}] %s`
      this._log = console.log.bind(console, message)
      this._info = console.info.bind(console, message)
      this._debug = console.debug.bind(console, message)
      this._trace = console.trace.bind(console, message)
      this._warn = console.warn.bind(console, message)
      this._error = console.error.bind(console, message)
    }
  }
}

@customElement("foo-elem" as any)
export class Foo extends LoggerMixin(BaseElement) {
  @property({ type: String })
  label?: string

  render() {
    this._log(this.label)
    return html`${this.label}`
  }
}

@sorvell The Constructor type declared in @polymer/lit-element/lib/decorators.d.ts causes some problems. It works fine, when I replace unknown with any in new(). The TypeScript compiler complains, when I try to replace any with unknown in LoggerMixin constructor.

export declare type Constructor<T> = {
    new (...args: unknown[]): T;
};

@frankiefu
Copy link
Contributor

Fixed in #228

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

No branches or pull requests

5 participants