键入一个通用的 websocket 处理程序

Typing a generic websocket handler

我正在尝试找到处理键入通用 websocket 处理程序的最佳方法。现有处理程序的实现如下所示:

type WSMessage = {
    type: string;
    [key: string]: unknown;
}
type HandlerFunc = (data: WSMessage) => void;

class PersistentWebsocket {
    subscriptions = {};

    initializeWebsocket(): void {
        this.socket.onmessage = (event) => {
            this.processMessage(JSON.parse(event.data) as WSMessage);
        };
    }

    processMessage(message: WSMessage): void {
        for(const handler of this.subscriptions[message.type] || []) {
            handler(message);
        }
    }
}

然后我有一个使用这个 websocket 的钩子:

export function useWebsocketSubscription(
    websocket: PersistentWebsocket,
    type: string,
    handler: HandlerFunc,
): void {
    useEffect(() => {
        websocket.subscribe(type, handler);
        return () => {
            websocket.unsubscribe(type, handler);
        };
    });
}

这样调用:

useWebsocketSubscription(websocket, "use-search-results", (data) => {           
    const d = (data as unknown) as PlannerTypes.SearchResults;                  
    setActivityOptions(d.results.activities);                                   
    setLocationOptions(d.results.locations);                                    
    setMode(DisplayMode.search);                                                
}); 

我真的很想能够删除 data as unknown as ActualType 声明,但我不知道如何编写泛型,让我定义在 websocket 消息的接口中传递的处理函数会符合。一切都在抱怨,因为 WSMessage 可能没有更具体的消息所期望的键,或者导致语法错误,或者打字稿不喜欢我在 PersistentWebsocket class.[=16 中访问 message.type =]

有没有办法让它工作,或者我是否在每个处理程序中都遇到了令人讨厌的 typecast/assertion?

我看不到什么 PlannerTypes.SearchResults,所以我不确定您为什么需要这样做 (data as unknown) as PlannerTypes.SearchResults。如果 PlannerTypes.SearchResultsWSMessage 的子类型(它应该是),你应该能够做到 data as PlannerTypes.SearchResults.

如果你想在每次有钩子时都避免强制转换,你可以为每个 websocket 类型写 overloaded declarations:

export function useWebsocketSubscription(
    websocket: PersistentWebsocket, 
    type: "use-search-results", 
    handler: (data: PlannerTypes.SearchResults) => void
): void;
export function useWebsocketSubscription(
    websocket: PersistentWebsocket, 
    type: string, 
    handler: HandlerFunc
): void { 
 ... definition ...
}