Search code examples
typescriptdecoratorweb-component

Strict type checking for property type with property decorator


Is there a way to validate the type of the property that is decorated in Typescript? I would like a property decorator that only works on boolean class properties, but not on e.g. string (example below). Is this possible?

(Note: I don't want runtime validation via reflect-metadata, just a compile type warning with Typescript.)

function booleanProperty<T extends HTMLElement>(target: T, prop: string) {
  // `target` will be the class' prototype
  Reflect.defineProperty(target, prop, {
    get(this: T): boolean {
      return this.hasAttribute(prop);
    },
    set(this: T, value: boolean): void {
      if (value) {
        this.setAttribute(prop, '');
      } else {
        this.removeAttribute(prop);
      }
    }
  });
}

class MyElement extends HTMLElement {
  @booleanProperty
  foo: boolean;

  @booleanProperty
  bar: string; // TS compile error
}
customElements.define('my-element', MyElement);

Solution

  • What about this?

    function booleanProperty<
      T extends HTMLElement & Record<K, boolean>,
      K extends string>(target: T, prop: K) {
        // ... impl here
    }
    

    The passed-in target needs to be an HTMLElement which has a boolean property at the key K, where K is the type of the passed-in prop. Let's see if it works:

    class MyElement extends HTMLElement {
      @booleanProperty // okay
      foo: boolean;
    
      @booleanProperty // error
      bar: string;
    }
    

    Looks good to me. Does that work for you?