1

I am trying to play with CustomElements in JavaScript without a framework, for educational purposes. I am trying to write a base class for my CustomElements, for loading a template and setting up a ShadowDOM. Since the template for a CustomElement depends on the actual implementation, I found myself wanting "abstract static properties".

abstract class CustomElement extends HTMLElement {
    abstract static template: HTMLTemplateElement;

    constructor() {
        super();
        this.shadow_root = this.attachShadow({ "mode": "open" });
        this.shadow_root.appendChild(this.constructor.template.content.cloneNode(true)); // How can can I tell typescript, that the constructor does in fact have a template property?
    }    
}

class CustomCircle extends CustomElement {
    static override template = loadTemplate(`CircleTemplate`); // I want this to be static (only do the loading work once, and enforce that everyone implementing "CustomElement" has it

    constructor() {
        super();
        ...
    }
}



class CustomRectangle extends CustomElement {
    static override template = loadTemplate(`RectangleTemplate`);

    constructor() {
        super();
        ...
    }
}

Now abstract static is not supported in TypeScript, what is my best shot at expressing every class extending CustomElement must have a (static) template property? (Extra: how do I tell TypeScript that this.constructor is guaranteed to have the template property?)

3
  • 2
    JavaScript doesn't have abstract at all. Maybe you should replace "JavaScript" with "TypeScript" here since that's the context in which abstract makes sense? Commented Jan 7, 2024 at 4:01
  • @jcalz: good point, I switched the question to typescript. In honesty I am mildly confused by the language versions (mostly javascript/ecmascript-X), and only found abstract via googling on some third party documentation site and was surprised that I could not find it on mdn. Commented Jan 7, 2024 at 4:18
  • some friendly soul added a link to this answer, unfortunately they removed their comment. While that would be a start, ideally I would not want my user to know about additional interfaces when extending. (After all the idea is to prevent implementation errors which happen due to lack of knowledge/oversight. If the errorchecker needs me to add extra typing (i.e. interfaces) when I use the class, then it seems like a diminished gain) Commented Jan 7, 2024 at 4:25

1 Answer 1

1

Based on an answer in here it seems the following should do as you require

interface CustomConstructor {
    new(): CustomElement;
    template: HTMLElement;
}
abstract class CustomElement extends HTMLElement {

    constructor() {
        super();
        this.shadow_root = this.attachShadow({ "mode": "open" });
        this.shadow_root.appendChild(this.constructor.template.content.cloneNode(true));
    }    
}

const CustomCircle: CustomConstructor = class CustomCircle extends CustomElement {
    static template = loadTemplate(`CircleTemplate`);

    constructor() {
        super();
    }
}



const CustomRectangle: CustomConstructor = class CustomRectangle extends CustomElement {
    static template = loadTemplate(`RectangleTemplate`);

    constructor() {
        super();
    }
}

If you play around in the code in TS Playground you'll see that TS will complain if a class does not have a static template

Please note: I am NOT a typescript coder, I dabble, but by no means am I at all proficient - I just saw that other answer and guessed what to do :p

Sign up to request clarification or add additional context in comments.

4 Comments

I edited your answer to reduce the number of typescript warnings (induced by my lacking MWE code), since the link is a bit long I took the liberty to edit the answer, hope that is alright with you.
@ted - all good, the TS Playground stuff was never meant to run :p
I see how this partly solves my question, do you have any idea how to get rid of the remaining error (that this.constructor dos not have a template property?). Also forgive me for not accepting the answer yet, while this is the only solution I have seen so far, it requires that a user remembers to use the magic syntax const CustomRectangle: CustomConstructor = class CustomRectangle, if a User simply extends the base he will not get warnings. This might be the best we can get, but I was hoping for more :)
@ted as I said, I'm no typescript coder :p

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.