Javascript 在Typescript中封装WebSocket消息

Javascript 在Typescript中封装WebSocket消息,javascript,typescript,types,websocket,encapsulation,Javascript,Typescript,Types,Websocket,Encapsulation,我正在尝试将websocket消息封装为定义良好的类型。 我有主IIncommingMessage,它是所有传入消息的基本接口,例如: export interface IIncommingMessage { className : IClassName; methodName : IMethodName; } 此websocket可以调用多种类型的类,如下所示: export type IClassName = IClassA | IClassB | IClassC 以及关联类中的

我正在尝试将websocket消息封装为定义良好的类型。
我有主IIncommingMessage,它是所有传入消息的基本接口,例如:

export interface IIncommingMessage {
  className : IClassName;
  methodName : IMethodName;
}
此websocket可以调用多种类型的类,如下所示:

export type IClassName = IClassA | IClassB | IClassC
以及关联类中的各种方法

export type IMethodName = IfooinClassA | IbarinClassA | IbazinClassB | IquxinClassB | IquuxinClassB | IcorgeinClassC
这样看起来像这样

ClassA:
  foo()
  bar()

ClassB:
  baz()
  qux()
  quux()

ClassC:
  corge
其思想是,如果websocket消息到达。它会来的

{
  className : "ClassB"
  methodName : "qux"
}
所以这应该调用函数qux()的ClassB

  • 我采取的方法看起来很糟糕。我想知道是否有更好的方法将web套接字消息紧密耦合到定义良好的类型

  • 还想知道我将如何在TypeScript中进行这个调用——它是prototype.call('className.method')吗


  • 关于你的第一部分有一个定义良好的类型,这是我将如何实现它

    class ClassA {
        foo() { }
        bar() { }
    }
    
    class ClassB {
        baz() { }
        qux() { }
        quux() { }
    }
    
    class ClassC {
        corge() { }
        notAMethod = 1;
    }
    
    // This will be a like a dictionary mapping name to class
    // Will also be used in the second part.
    const Classes = {
        ClassA,
        ClassB,
        ClassC,
    };
    
    // This will be 'ClassA'|'ClassB'|'ClassC'
    type IClassName = keyof typeof Classes;
    type IClassOf<T extends IClassName> = InstanceType<typeof Classes[T]>;
    type MethodFilter<T extends IClassName> = { [MN in keyof IClassOf<T>]: IClassOf<T>[MN] extends () => void ? MN : never }
    type MethodName<T extends IClassName> = MethodFilter<T>[keyof MethodFilter<T>];
    
    interface IGenericIncomingMessage<T extends IClassName> {
        className: T;
        methodName: MethodName<T>;
    }
    
    type IIncomingMessage = IGenericIncomingMessage<'ClassA'> | IGenericIncomingMessage<'ClassB'> | IGenericIncomingMessage<'ClassC'>;
    
    let msg0: IIncomingMessage = {
        className: 'ClassA',
        methodName: 'foo', // valid
    }
    
    let msg1: IIncomingMessage = {
        className: 'ClassC',
        methodName: 'corge', // valid
    }
    
    let msg2: IIncomingMessage = { // compiler error. Type ... is not assignable to type 'IIncomingMessage'.
        className: 'ClassA',
        methodName: 'corge', 
    }
    
    let msg3: IIncomingMessage = {
        className: 'ClassD', // compiler error. ClassD Name is not not in 'ClassA' | 'ClassB' | 'ClassC'
        methodName: 'corge',
    }
    
    let msg4: IIncomingMessage = {
        className: 'ClassC',
        methodName: 'notAMethod', // compiler error. Type '"notAMethod"' is not assignable to type '"foo" | "bar" | "baz" | "qux" | "quux" | "corge"'.
    }
    
    A类{
    foo(){}
    bar(){}
    }
    B类{
    baz(){}
    qux(){}
    quux(){}
    }
    C类{
    corge(){}
    方法=1;
    }
    //这将类似于将名称映射到类的字典
    //也将在第二部分中使用。
    常量类={
    甲级,
    B类,
    C类,
    };
    //这将是“ClassA”|“ClassB”|“ClassC”
    类型IClassName=typeof类的键;
    IClassOf类型=InstanceType;
    类型MethodFilter={[MN-in-keyof-IClassOf]:IClassOf[MN]扩展()=>void?MN:never}
    类型MethodName=MethodFilter[keyof MethodFilter];
    接口IGenericIncomingMessage{
    类名:T;
    方法名称:方法名称;
    }
    IIncomingMessage类型=IGenericIncomingMessage | IGenericIncomingMessage | IGenericIncomingMessage;
    让msg0:IIncomingMessage={
    className:'ClassA',
    methodName:'foo',//有效
    }
    让msg1:IIncomingMessage={
    className:'ClassC',
    methodName:'corge',//有效
    }
    让msg2:IIncomingMessage={//编译器错误。类型…不可分配给类型“IIncomingMessage”。
    className:'ClassA',
    methodName:'corge',
    }
    让msg3:IIncomingMessage={
    className:'ClassD',//编译器错误。ClassD名称不在'ClassA'|'ClassB'|'ClassC'中
    methodName:'corge',
    }
    让msg4:IIncomingMessage={
    className:'ClassC',
    methodName:'notAMethod',//编译器错误。类型'notAMethod'不可分配给类型'foo'|“bar”|“baz”|“qux”|“qux”|“corge”'。
    }
    
    关于第二部分,我使用前面定义的类字典按名称查找类,创建类的新实例。这意味着字典中没有有效类名的恶意消息将无法工作

    // I omit error handling here.
    function invokeFunction<T extends IClassName>(message: IGenericIncomingMessage<T>): void {
        // Look for the class instance in dictionary and create an instance
        const instance = new Classes[message.className]() as IClassOf<T>;
        // Find the method by name. The cast to any is to silence a compiler error;
        // You may need to perform additional login to validate that the method is allowed.
        const fn = instance[message.methodName] as unknown as () => void;
        fn.apply(instance);
    }
    
    //我在这里省略了错误处理。
    函数invokeFunction(消息:IGenericIncomingMessage):void{
    //在字典中查找类实例并创建一个实例
    const instance=新类[message.className]()作为IClassOf;
    //按名称查找方法。强制转换为any是为了消除编译器错误;
    //您可能需要执行其他登录以验证该方法是否被允许。
    const fn=实例[message.methodName]未知为()=>void;
    fn.适用(实例);
    }
    
    这些方法是静态的吗?它们必须是类吗?如果不强制进入静态状态,它就不能工作吗?是的,它们是不同的类,将处理来自websocket连接的特定消息这太棒了!!!只需要一个简单的问题来重新评价一下。如果类名类似于
    Class.A
    除了有一个映射文件外,还有什么方法可以将ClassA映射到message
    {className:“ClassA”;methodName:“foo”}
    呢?在这种情况下,您必须手动指定Classes对象中的名称:
    const Classes={'ClassA':Class.A,'ClassB':Class.B,'ClassC':Class.C,}
    Francis的建议在这种情况下是可行的。他还做了一些很好的编辑来改进我的答案。谢谢Francis。非常感谢SherifElmetainy和@Francis duvivier!!!非常感谢!对于无知的问题,很抱歉。是否可以从
    let class={'ClassA':Class.A,'ClassB':Class.B,'ClassC':Class.C,};
    我认为它会像一个集合一样工作,我可以在其中使用Classes.push或Classes.add,但它似乎不起作用,因为它是一个对象。不确定是否有办法动态地添加到此Classes变量