Ron*_*ton 7 strong-typing discriminated-union typescript
我有一个用于通过套接字连接来回传递 JSON 消息的系统。它使用标记联合来表示消息类型:
export type ErrorMessage = { kind: 'error', errorMessage: ErrorData };
export type UserJoined = { kind: 'user-joined', user: UserData };
// etc
export type Message = ErrorMessage | UserJoined | /*etc*/;
Run Code Online (Sandbox Code Playgroud)
它在基本代码中运行得相当好,但我在其之上构建了一个模块,我想扩展代码。我要添加一个新的消息类型:
export type UserAction = { kind: 'user-action', action: Action }
Run Code Online (Sandbox Code Playgroud)
这里的问题是我无法扩展“Message”以将我的新 UserAction 包含到联合中。我想我可以制作自己的扩展消息:
export type ExtendedMessage = Message | UserAction;
Run Code Online (Sandbox Code Playgroud)
但这里的问题是,第一,这看起来很笨拙。我无法将新的 UserAction 传递到任何需要消息的方法中,即使代码实际上应该完全正常工作。以后想要扩展我的模块和基本模块的任何人都需要创建第三种类型:export type ExtendedMessageAgain = ExtendedMessage | MyNewMessage。
所以。我已经看到通过添加新的 .d.ts 文件来扩展附加属性的接口(例如 Passport 如何扩展 Express JS 的 Request 对象以添加身份验证属性),我认为标记联合也必须存在类似的东西,对吗?
但事实似乎并非如此。我用谷歌搜索了一下,没有看到这种模式在任何地方使用。这让我相信我的设计也许在某种程度上是错误的。但我没有看到解决办法。
我不想使用类,因为类型信息会通过网络被删除;该kind财产必须存在。我喜欢这个范例:
declare var sendMessage = (message: Message) => void;
sendMessage( { kind: 'error', errorMessage: { /* */ } }); // ok
sendMessage( { kind: 'random', parameter: { /* */ } }); // error, no kind 'random'
sendMessage( { kind: 'error', message: { /* */ } }); // error, no property 'message' on 'error'
Run Code Online (Sandbox Code Playgroud)
但我看到的唯一解决方案是制作Message一个界面基础,如下所示:
export interface Message { kind: string }
export interface ErrorMessage extends Message { errorMessage: ErrorData }
declare var sendMessage = (message: Message) => void;
sendMessage( { kind: 'error', errorMessage: { /* */ } }); // ok
sendMessage( { kind: 'random', parameter: { /* */ } }); // ok
sendMessage( { kind: 'error', message: { /* */ } }); // ok
Run Code Online (Sandbox Code Playgroud)
这种方法失去了上面所有好的类型保护。
那么...有没有一种方法可以在多个模块中扩展标记联合,从而影响类型的原始名称,并且无需定义新类型?或者这里有我没有看到的更好的设计吗?
以下是启发这篇文章的代码: https: //github.com/RonPenton/NotaMUD/blob/master/src/server/messages/index.ts
我希望对其进行大规模重构,以便我可以将所有消息移至单独的模块中,而不是随着时间的推移这个文件变得一团糟。
您可以执行此操作来定义 的联合类型Message:
export interface MessageTypes {}
export type Message = MessageTypes[keyof MessageTypes]
Run Code Online (Sandbox Code Playgroud)
然后,无论您在何处定义新消息类型,请执行以下操作:
export type UserAction = { kind: 'user-action', action: Action }
declare module '../message' { // Where you define MessageTypes
interface MessageTypes {
UserAction: UserAction
}
}
Run Code Online (Sandbox Code Playgroud)
因此,接口的值MessageTypes成为联合类型,您可以使用声明合并向接口添加更多值,这将自动更新联合类型。
您可以查看 TS 文档以获取有关声明合并的更多信息: https: //www.typescriptlang.org/docs/handbook/declaration-merging.html
| 归档时间: |
|
| 查看次数: |
1184 次 |
| 最近记录: |