2

I have some third party framework with functionality to add an event listener on the server and call that event from the client side.

That look like this for server:

framework.addEvent('myEvent1', (clientId, myParam1, myParam2...) => { my server work here })

And on client side:

let result = await framework.callRemote('myEvent1', myParam1, myParam2,...)

I do not understand how to correctly and conveniently describe the types of my events, their string names and associated callback types that I could use on the server and client sides (by importing these types from the global module).

Each callback on the server side always has a clientId as first argument, but next my parameters and their types may be different for each event.

I only came up this:

export namespace ServerEvent {

    export enum Name {            
        MyEvent1 = 'MyEvent1',
        MyEvent2 = 'MyEvent2',
        MyEvent3 = 'MyEvent3',           
    }

    export namespace Func {            
        export type MyEvent1 = (myArg1: string) => void            
        export type MyEvent2 = (myArg1: number, myArg2?: boolean) => void            
        export type MyEvent3 = () => void
    }    
}

But even this I don't understand how to use it to see all arguments and their types on server and client side.

I found some example of which perhaps solves my problem, but I cannot grasp it and how to move in this direction:

const wrapper = <U extends (...args: any[]) => any>(func: U) => (...args: Parameters<U>) : ReturnType<U> => func(...args);
  • [This](https://stackoverflow.com/questions/70137328/mapping-a-variable-number-of-generics-while-retaining-link-between-type-values/70138046#70138046) answer is related . Let me know if it works for you. Also you can check my [article](https://catchts.com/publish-subscribe) – captain-yossarian from Ukraine Nov 29 '21 at 07:39
  • 1
    @captain-yossarian Thanks! This completely solves my question. I will rewrite my temporary solution by analogy with this example. – Alexandr Kat Jan 26 '22 at 18:24

1 Answers1

0

I found some solution, I suspect not the best, but I almost achieved my goal:

// GLOBAL Module

export enum ServerEventName {
    Event1 = 'MyEvent1',
    Event2 = 'MyEvent2',
    Event3 = 'MyEvent3',
    Event4 = 'MyEvent4', // events can be withot callback type if they has no other arguments besides clientId
}

export namespace ServerCallback {
    export type Event1Callback = (arg1: string) => void
    export type Event2Callback = (arg1: number, arg2?: boolean) => void
    export type Event3Callback = () => void
}

// SERVER

function addEvent<U extends (...args: any[]) => any>(name: string, callback: (clientId: number, ...args: Parameters<U>) => ReturnType<U>) {
    someFramework.addEvent(name, (clientId: number, ...args: Parameters<U>) => { callback(clientId, ...args) })
}

addEvent<ServerCallback.Event1Callback>(ServerEventName.Event1, (clientId, arg1) => {
    console.log(clientId, arg1)
})
addEvent<ServerCallback.Event2Callback>(ServerEventName.Event2, (clientId, arg1, arg2) => {
    console.log(clientId, arg1, arg2)
})
addEvent<ServerCallback.Event3Callback>(ServerEventName.Event3, (clientId) => {
    console.log(clientId)
})


// CLIENT 

function callRemoteEvent<U extends (...args: any[]) => any>(name: string, ...args: Parameters<U>) {
    someFramework.callRemote(name, ...args)
}

callRemoteEvent<ServerCallback.Event1Callback>(ServerEventName.Event1, 'this is string')
callRemoteEvent<ServerCallback.Event2Callback>(ServerEventName.Event2, 123, true)
callRemoteEvent<ServerCallback.Event3Callback>(ServerEventName.Event3)
callRemoteEvent(ServerEventName.Event4)

IDE syntax helpers successfully recognize types and inspect them.

However, there are some drawbacks. Events name enum are not related to theyr callback types, and need to indicate them correctly together.

UPD: this This answer completely solves my question.