类型安全
使用 TypeScript 时,为 WebSocket 通信带来类型安全至关重要,这也包括你的处理程序!话虽如此,MSW 有意不支持任何类型参数来注释传出/传入事件:
¥Bringing type safety to the WebSocket communication is essential when using TypeScript, and that includes your handlers too! That being said, MSW intentionally doesn’t support any type arguments to annotate the outgoing/incoming events:
import { ws } from 'msw'
ws.link<Arg1, Arg2>(url)
// ^^^^^^^^^^^^ Type error!
此决定背后的原因有两个:
¥The reasoning behing this decision is twofold:
-
缩小数据类型并不能保证不会通过网络发送不同的数据类型(经典类型与运行时争论);
¥Narrowing down the data type doesn’t guarantee that a different data type wouldn’t be sent over the network (the classic types vs runtime debate);
-
你在消息事件监听器中收到的
event.data
值将始终为string | Blob | ArrayBuffer
类型,因为 MSW 不提供消息解析。¥The
event.data
value you receive in the message event listener will always be of typestring | Blob | ArrayBuffer
because MSW provides no message parsing.
如果你使用对象与 WebSocket 服务器通信,则在发送和接收这些对象时必须分别对这些对象进行字符串化和解析,这已经意味着你的应用中存在解析层。
¥If you are using objects to communicate with a WebSocket server, those objects have to be stringified and parsed when sending and receiving them, respectively, which already implies a parsing layer being present in your application.
你可以通过引入解析实用程序在 WebSockets 中实现适当的类型和运行时安全性。像 Zod 这样的库可以极大地帮助你实现类型和运行时安全。
¥You can achieve a proper type and runtime safety in WebSockets by introducing parsing utilities. Libraries like Zod can help you greatly in achieving type and runtime safety.
import { z } from 'zod'
// Define a Zod schema for the incoming events.
// Here, our WebSocket communication supports two
// events: "chat/join" and "chat/message".
const incomingSchema = z.union([
z.object({
type: z.literal('chat/join'),
user: userSchema,
}),
z.object({
type: z.literal('chat/message'),
message: z.object({
text: z.string(),
sentAt: z.string().datetime(),
}),
}),
])
chat.addEventListener('connection', ({ client, server }) => {
client.addEventListener('message', (event) => {
const result = incomingSchema.safeParse(event.data)
// Ignore non-matching events.
if (!result.success) {
return
}
const message = result.data
// Handle incoming events in type-safe way.
switch (message.type) {
case 'chat/join': {
// ...
break
}
case 'chat/message': {
// ...
break
}
}
})
})
请随意为消息事件引入一个高阶监听器,以抽象该解析,帮助你在处理程序中重用它。
¥Feel free to introduce a higher-order listener for the message event that abstracts that parsing, helping you reuse it across your handlers.