假设我有以下类型(语法类似于 Elm/Haskell):
type Reply = LoginReply | LogoutReply
type LoginReply = LoginSucceeded | AlreadyLoggedIn String
如果我尝试使用 Typescript 的可区分联合对其进行建模,我将面临以下问题:LoginReply
需要具有值为“loginReply”的属性 kind
,但它可以不是因为它是使用 type
关键字声明的,而不是 class
。
这是迄今为止我解决这个问题的最佳方法:
type Reply = LoginReply | LogoutReply
type LoginReply = LoginSucceeded | AlreadyLoggedIn
interface LoginSucceeded {
kind: "loginSucceeded";
}
interface AlreadyLoggedIn {
kind: "alreadyLoggedIn";
loggedInUsername: string;
}
interface LogoutReply {
kind: "logoutReply";
}
如您所见,人们甚至不能在任何地方使用“loginReply”
,因此不能用于歧视。
要知道 Reply
变量是 LoginReply
,我唯一的解决方法是查看其 kind
是否是 之一”登录成功”
和 “alreadyLoggedIn”
。
那么,我如何在 Typescript 中实现我想要的目标呢? 如何创建一个可区分联合类型,其子类型本身就是可区分联合类型?
最佳答案
因为 LoginReply
只是 LoginSucceeded | 的联合。 AlreadyLoggedIn
,从类型系统的角度来看,type Reply = LoginReply | 之间没有区别。 LogoutReply
和 type Reply = LoginSucceeded |已经登录 |注销回复
。 type
关键字引入了一个类型别名,顾名思义,它只是类型的一个方便名称,本身并不是一个新类型。
你有两个选择,但都不是 100% 你想要的。
您可以检查 LoginReply
的所有可能值 kind
,正如您提到的您考虑的那样:
function doStuff(o: Reply) {
switch(o.kind)
{
case 'alreadyLoggedIn' :
case 'loginSucceeded' :
o; /* is LoginReply */ break;
case 'logoutReply': o // o is LogoutReply
}
}
或者您可以为 LoginReply
子类型添加额外的 subKind
字段:
export type Reply = LoginReply | LogoutReply
type LoginReply = LoginSucceeded | AlreadyLoggedIn
interface LoginSucceeded {
subKind: "loginSucceeded";
kind: "loginReply";
}
interface AlreadyLoggedIn {
kind: "loginReply";
subKind: "alreadyLoggedIn";
loggedInUsername: string;
}
interface LogoutReply {
kind: "logoutReply";
logout: boolean
}
function doStuff(o: Reply) {
switch(o.kind)
{
case 'loginReply' : o // is LoginReply
switch(o.subKind) {
case 'alreadyLoggedIn' : o; /* is AlreadyLoggedIn */ break;
case 'loginSucceeded' : o; /* is LoginSucceeded */ break;
}
break;
case 'logoutReply': o // LogoutReply
}
}
关于Typescript 嵌套标记/区分联合类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51744218/