javascripttypescriptsubclasslit-elementjavascript-decorators

Override Property Typing of Lit-Element Subclasses in TypeScript


When subclassing a lit-element Class to add further typing, should the @property decorator be overridden or just the type and initializer? In otherwords, suppose I have this code:

interface AB {
   a: number,
   b: string,
}

@customElement('my-parent')
class MyParent extends LitElement {
    @property({type: Array}) stuff: readonly any[] = [];
}

which of the following would be right as a way of subclassing:

@customElement('my-child')
class MyChild extends MyParent {
    override @property({type: Array}) stuff: readonly Readonly<AB>[] = [];
}

or

@customElement('my-child')
class MyChild extends MyParent {
    override stuff: readonly Readonly<AB>[] = [];
}

Both seem to be working in my codebase, so I'm not sure which to standardize to.


Solution

  • Both syntax working I think is more of a quirk of TypeScript's original implementation of class fields with useDefineForClassFields: false where "overridden" class fields still end up invoking accessor added by the decorator on a superclass field.

    Semantically it makes more sense that overridden properties must also be decorated separately and shouldn't inherit previously decorated behavior. Thus your first case (using @property in base and subclass) will be the only one that works when using standard decorators for Lit reactive properties which will require the accessor keyword to turn class fields into accessors.

    See the TypeScript 3.7 Announcement, for more details, especially the section, "This can cause quite a bit of fallout for existing code that use inheritance. First of all, set accessors from base classes won’t get triggered - they’ll be completely overwritten."

    Note that useDefineForClassFields is switched to true by default if the lib includes "es2022" or later or "esNext".


    Below is an older version of my answer which was incorrect.

    It's odd that you're marking @property on something that's readonly since the point of @property is to make it reactive to trigger an update on set, which readonly implies you won't do.

    In any case, overriding the class property in a subclass without @property will remove the reactivity.