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);
}