#Abstract method arguments types not inherited in child class

25 messages · Page 1 of 1 (latest)

fiery heath
#

I've got an interface representing the arguments (along with their types) passed to a listener for each event type

export interface ClientEvents {
  applicationCommandPermissionsUpdate: [data: ApplicationCommandPermissionsUpdateData];
  autoModerationActionExecution: [autoModerationActionExecution: AutoModerationActionExecution];
  autoModerationRuleCreate: [autoModerationRule: AutoModerationRule];
  autoModerationRuleDelete: [autoModerationRule: AutoModerationRule];
  autoModerationRuleUpdate: [
    oldAutoModerationRule: AutoModerationRule | null,
    newAutoModerationRule: AutoModerationRule,
  ];
  ...

and two classes, an abstract parent:

export abstract class BaseEvent<K extends keyof ClientEvents> extends Base {
    public readonly name : K;
    protected constructor (client : Client, name : K, once = false) {
        super(client);
        this.name = name;
    }

    public abstract listener (...args : ClientEvents[K]) : Awaitable<void>; // awaitable may or may not be a promise
}

and a child:

export default class MessageCreateEvent extends BaseEvent<'messageCreate'> {
    public constructor (client : Client) {
        super(client, 'messageCreate'); // not super-convinient that I have to type in the same event name twice but I can't use a type as a value so ¯\_(ツ)_/¯
    }
    public listener (message) : void { // HERE is the issue !!!
        console.log(this.client.user?.id);
    }

}

So why doesn't my editor show the typings for listener method arguments in the child class (where I typed !!!)?

weary grove
#

@fiery heath Subclass methods don't really infer types from their parent types or interfaces methods.

#

For one, because subclasses methods can have more specific types than their parents; but also I think it was something that was attempted in the TS development process and it ended up not working well.

fiery heath
weary grove
#

I don't know if there's anything simple that's significantly cleaner than just annotating the arguments.

#

In theory, maybe you could make a function that generates these classes:

function buildMessageHandlerClass = <K extends keyof ClientEvents>(event: K, handleEvent: /*...*/) => {
    return class extends BaseEvent<K> {
         listener(/*...*/) {
              handleEvent(/*...*/)
         }
    }
}
#

Also maybe worth considering if you could use a non-class based approach here.

fiery heath
#

At least the types show properly

fiery heath
fiery heath
#

How is it missing those properties, if I'm calling super?

#

I can use as unknown as BaseEvent<Event> on the anonymous class, but I don't see why I have to do that - the class is literally a child of BaseEvent, so naturally it must have all the properties BaseEvent had.

#

Or am I missing something?

weary grove
#

Can you make a playground reproduction?

fiery heath
#

Sure, one moment.

edgy merlinBOT
#
jedrek_0429#0

Preview:```ts
...
return class extends BaseEvent<Event> {
public constructor(name: Event, once: boolean) {
super(name, once)
}

  public listener(
    ...args: ClientEvents[Event]
  ): Awaitable<void> {
    return callback(...args)
  }
}

...```

fiery heath
#

@weary grove, there you go.

#

But it works!

weary grove
#

@fiery heath Oh, your return type says you're returning an instance of the class, not a class.

fiery heath
#

Ohhh

#

Yeah

weary grove
#

Change the return type to : typeof BaseEvent<Event> and it should work.