Home > Back-end >  Can I have a class with arbitrary functions in TypeScript?
Can I have a class with arbitrary functions in TypeScript?

Time:12-16

I am new to TypeScript, but loving it!

In JavaScript, if I want to check if an object has a function and call it, I could do:

if(this['my_func']) {
  this['my_func']();
}

In TypeScript, it does not seem to work this way because for that code to be valid, my_func must be declared. In my case, my_func may not be declared. Specifically, a subclass can choose to declare/define it or not, and I want it to be called only if it is defined.

I know I can add @ts-ignore, but I am hoping there is a better (right) way.

Is there a way to say that an object may contain non-explicit declarations?

Here is my full code:

import {LitElement} from 'lit';

export type AnyConstructor<T = object> = new(...args: any[]) => T;

type EventType = keyof HTMLElementEventMap;
type EventValue = ValueOf<HTMLElementEventMap>;

export abstract class EventClass extends LitElement {
}

export const EventMixin = <T extends AnyConstructor<EventClass>>(
    events: EventType[],
    superClass: T
): T & AnyConstructor<EventClass> => {
  class Event_ extends superClass {

    public override connectedCallback(): void {
      super.connectedCallback();

      events.forEach((event: EventType) => {
        const listener: any = this[`_${event}`];

        if(listener) {
          this.addEventListener(event, (event: EventValue) => listener(event));
        }
      });
    }

  }

  return Event_;
};

The goal is to allow a subclass to define functions such as _onClick(event: MouseEvent), which are registered as listeners when the element is connected. This may not be a good idea, I know. I am just messing around with TypeScript, and am open to feedback/suggestions!

CodePudding user response:

The right way is with type guards / type predicates:

type MethodNamed<Key extends string> = { [K in Key]: Function };
function hasMethod<T, K extends string>(obj: T, methodName: K): obj is T & MethodNamed<K> {
  return obj != null && typeof obj === 'object' && typeof (obj as any)[methodName] === 'function';
}

Example of usage:

declare const foo: unknown;

if (hasMethod(foo, 'cool')) {
  foo.cool(1, 2, 3);
}
  • Related