Skip to content

Convert HTMLElement and friends to classes to support Web Components/Custom elements #574

Description

Please model HTMLElement and its subclasses as classes instead of interfaces in lib.d.ts to make it possible to subclass them.

My motivation is custom elements and web components. To create a custom element you need to create a subclass of HTMLElement and register it with the browser. Currently, HTMLElement etc are interfaces and can't be subclassed. I've been using a hacked lib.d.ts in my pet TypeScript project which uses web components.

Tutorial and info about how to use custom elements is here: http://www.html5rocks.com/en/tutorials/webcomponents/customelements/

Activity

  1. am11 commented on Aug 31, 2014

    @am11

    👍

    Another reason to implement support for abstract class in TypeScript: #6 😃

  2. danquirk commented on Sep 2, 2014

    @danquirk
    Member

    See #299, we'll be revisiting lib.d.ts changes in the near future and try to tackle some of these then.

  3. jamiewinder commented on Feb 26, 2015

    @jamiewinder

    Any update on this? Is there a clean way to write custom elements in TypeScript at the moment?

  4. sothmann commented on May 5, 2015

    @sothmann

    Any update on this one?

  5. mhegazy commented on May 5, 2015

    @mhegazy
    Contributor

    We have a few suggestions to make this work.. we first need #2961, #2959 and possibly,
    #2957 to make the change to the library..

    Also related #2341 to ensure native types with uncallable constructors are modeled correctly, e.g. Symbol or HTMLElements.

    #1168 tracks doing the same for non-DOM native types.

  6. Ciantic commented on May 7, 2015

    @Ciantic

    👍

    For what it's worth, here is my ancient hack to get inheritance working with custom elements: https://github.com/proxy/gist.github.com/Ciantic/9db1b6281bd7a743ffb5

  7. benlesh commented on May 14, 2015

    @benlesh

    This is a killer. with babel/ES6 I can easily do the following to create a WebComponent:

    class MyComponent extends HTMLElement {
    
    }

    But in TypeScript it complains that I can't extend because HTMLElement is not a class. It is indeed a class.

  8. mhegazy commented on May 21, 2015

    @mhegazy
    Contributor

    This should be fixed in TypeScript 1.6

  9. added
    CommittedThe team has roadmapped this issue
    and removed
    RevisitAn issue worth coming back to
    on May 21, 2015
  10. mhegazy commented on May 21, 2015

    @mhegazy
    Contributor

    Also see #1168.

  11. basarat commented on Jun 23, 2015

    @basarat
    Contributor

    This can be closed thanks to : #3516 just tested this with atom-typescript@4.6.0

    class MyComponent extends HTMLElement {    
    }

    image

  12. 17 remaining items

  13. mhegazy commented on Dec 3, 2015

    @mhegazy
    Contributor

    i think they should all be classes... eventually.

  14. 0815fox commented on Jul 11, 2016

    @0815fox

    I just ran into this issue as well. The problem is, that people are mislead somehow, because they see the examples on MDN or on Custom Elements spec - working draft, where it is suggested to create an instance of the new, own webcomponent like this:

    var myBtn = new MySaveBtn;
    document.querySelector('#placeholder').appendChild(myBtn);
    

    Or from the working draft: "Finally, we can also use the custom element constructor itself. That is, the above code is equivalent to:"

    const flagIcon = new FlagIcon()
    flagIcon.country = "jp"
    document.body.appendChild(flagIcon)
    

    So, the need for a non-callable constructor is there! For the moment I just put the super call into a try-catcher:

    constructor() {
      try {super();} catch(e) {}
      ...
    }
    
  15. 0815fox commented on Jul 11, 2016

    @0815fox

    Okay, for others, who struggle with this as well... The MDN source seems to reflect the current way it is implemented by webcomponents.js and chrome is, that document.registerElement returns a constructor, which you then can use to call:

    class FooElement extends HTMLElement {
      ...
    }
    const FooElementCtor = document.registerElement('element-name',FooElement);
    const FooInstance = new FooElementCtor();
    document.querySelector('body').appendChild(FooInstance);
    

    Just in case anyone else struggles with this... Using the constructor of FooElement directly will throw an Invalid Constructor-exception. As I generate the DOM completely programatically, I do not use the element-name as tag at all, but it probably has to be a valid tag name containing at least one dash - - character. The example as shown above works for me.
    However, the W3C draft as of 24 June 2016 assumes, that calling the constructor directly may be supported in future, or it is just an error in the spec.

  16. ZanderBrown commented on Dec 8, 2016

    @ZanderBrown

    Reading the "What's New" for 2.1 there seems to be some new feature for working with Custom Elements but i don't seem to be able to locate any example of it's usage

  17. saschanaz commented on Apr 3, 2017

    @saschanaz
    Contributor

    i think they should all be classes... eventually.

    Are there any blocking problems not to do this now? microsoft/TypeScript-DOM-lib-generator#222

    #2957 is still open but seems not really a blocking problem. In #563 (comment):

    The current workaround of interface + prototype.method = ... does enable the generated-code scenario just as well as partial class would.

  18. saschanaz commented on Apr 24, 2017

    @saschanaz
    Contributor

    From #15348 (comment)

    I think we should add classes regardless. that is covered by #574

    Mohamed Hegazy (@mhegazy) You mean you will accept a PR for this?

  19. mhegazy commented on Apr 24, 2017

    @mhegazy
    Contributor

    I think we would. my concerns would be back compat. so we will need to run the new chance on DT, and on our internal test suite to make sure there are no breaks we are not aware of. if all passes, do not see why we can not take the change.

  20. saschanaz commented on Apr 24, 2017

    @saschanaz
    Contributor

    Hmm, DT will be a best place for a first PR then 😃

  21. mhegazy commented on Apr 24, 2017

    @mhegazy
    Contributor

    Hmm, DT will be a best place for a first PR then

    Not sure what you mean? i meant make the change to https://github.com/Microsoft/TSJS-lib-generator, build a custom TS version with the new library, then compile all DT types with the custom TS drop. ideally you should not see any errors.

  22. saschanaz commented on Apr 24, 2017

    @saschanaz
    Contributor

    I thought you would publish a new lib.d.ts on DefinitelyTyped and get user feedback. My misunderstanding.

  23. xt0rted commented on Sep 17, 2018

    @xt0rted

    I'm trying to put together a definition for github/query-selector but I'm unable to use the generic type on the klass parameter because of how Element, HTMLElement, etc. are typed. For now I'm having to use any which isn't really ideal.

    declare module '@github/query-selector' {
        type Queryable = Document | DocumentFragment | Element;
    
        export function query<T extends Element = Element>(context: Queryable, selectors: string, klass?: any): T;
    
        export function querySelectorAll<T extends Element = Element>(context: Queryable, selector: string, klass?: any): Array<T>;
    }

    Are Element etc. going to be changed to classes so scenarios like this work?

  24. freshgum-bubbles commented on Apr 3, 2024

    @freshgum-bubbles

    Not to necro, but is there something I'm missing here? This is a fairly standard interface by now, and not having typings for connectedCallback, attributeChangedCallback seems like an odd oversight (especially with the popularity of web components).

    Everything else related to this seems to be typed, such as ShadowRoot & co.

    I would have assumed that this would have been implemented via some sort of CustomHTMLElement-esque interface that types all the standard WC methods, like so:

    interface CustomHTMLElement {
      connectedCallback?(): void;
      disconnectedCallback?(): void;
      adoptedCallback?(): void;
      attributeChangedCallback?(name: string, oldValue: string, newValue: string): void;
    }

    Is this a deliberate omission, or have the TS team just not got around to it yet?
    Is it outside the scope of lib.dom.d.ts?

    Re: DefinitelyTyped, I've searched the codebase and all I can find are
    a bunch of re-implementations of the same interface as above.

    If this is a separate issue, I'm happy to post it as such -- this is the closest related issue
    that I could find here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Domain: lib.d.tsThe issue relates to the different libraries shipped with TypeScriptIn DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions