Javascript 在Typescript中封装WebSocket消息
我正在尝试将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 以及关联类中的
我有主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
关于你的第一部分有一个定义良好的类型,这是我将如何实现它
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变量