我有一个系统用于通过套接字连接来回传递JSON消息。它使用Tagged Unions作为消息类型:
export type ErrorMessage = { kind: 'error', errorMessage: ErrorData };
export type UserJoined = { kind: 'user-joined', user: UserData };
// etc
export type Message = ErrorMessage | UserJoined | /*etc*/;
它在基本代码中运行得相当不错,但我有一个基于它构建的模块,我想扩展代码。我有一个要添加的新消息类型:
export type UserAction = { kind: 'user-action', action: Action }
问题在于我无法扩展“Message”以将新的UserAction包含到union中。我想我可以发表自己的扩展信息:
export type ExtendedMessage = Message | UserAction;
但这里的问题似乎是笨重,排名第一。我不能将我的新UserAction传递给任何期望消息的方法,即使代码实际上应该完全正常工作。其他任何想要扩展我的模块和基本模块的人都需要创建第三种类型:export type ExtendedMessageAgain = ExtendedMessage | MyNewMessage
。
因此。我已经看到通过添加新的.d.ts文件扩展了其他属性的接口(比如护照如何扩展Express JS的Request对象以添加身份验证属性),我认为这样的东西也必须存在于标记的联合,对吧? / p>
但事实似乎并非如此。我四处搜索,并没有看到这种模式在任何地方使用。这让我相信也许我的设计在某种程度上是错误的。但我没有看到解决方法。
我不想使用类,因为类型信息是通过线路擦除的; 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'
但我所看到的唯一解决方案是将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
这种方法失去了上面所有类型的保护。
那么......有没有办法在多个模块中扩展Tagged Unions,影响该类型的原始名称,而无需定义新类型?或者这里有一个更好的设计,我只是没有看到?
以下是启发此帖子的代码:https://github.com/RonPenton/NotaMUD/blob/master/src/server/messages/index.ts
我正在寻求大规模重构,因此我可以将所有消息移到单独的模块中,而不是随着时间的推移,这个文件逐渐变得不合时宜。
答案 0 :(得分:0)
您可以执行以下操作来定义Message
的并集类型:
export interface MessageTypes {}
export type Message = MessageTypes[keyof MessageTypes]
然后在定义新消息类型的任何地方执行以下操作:
export type UserAction = { kind: 'user-action', action: Action }
declare module '../message' { // Where you define MessageTypes
interface MessageTypes {
UserAction: UserAction
}
}
因此MessageTypes
接口的值成为联合类型,您可以使用声明合并将更多值添加到接口,这将自动更新联合类型。
您可以查看TS文档以获取有关声明合并的更多信息:https://www.typescriptlang.org/docs/handbook/declaration-merging.html