假设我有以下几种类型(语法为Elm-ish / Haskell-ish):
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
是"loginSucceeded"
和"alreadyLoggedIn"
之一。 / p>
那么,如何实现Typescript中想要的功能? 如何创建其子类型为歧视联合类型的歧视联合类型?
答案 0 :(得分:2)
由于<div class="col-lg-4">
<div class="panel-group" id="duyuruGosterme">
<div class="panel">
@foreach (var item in Model)
{
<div class="panel-heading bg-blue-800">
<h6 class="panel-title">
<a data-parent="#duyuruGosterme" data-toggle="collapse" href="#@item.ID">@item.Baslik <span class="pull-right label label-flat label-rounded border-white">@item.YayinTarihi.ToString("dd MM yyyy")</span></a>
</h6>
</div>
<div id="@item.ID" class="panel-collapse collapse">
<div class="panel-body">
<p>@Html.Raw(item.icerik)</p>
</div>
</div>
}
</div>
</div>
</div>
只是LoginReply
的并集,因此从类型系统角度来看,LoginSucceeded | AlreadyLoggedIn
和type Reply = LoginReply | LogoutReply
之间没有区别。 type Reply = LoginSucceeded | AlreadyLoggedIn | LogoutReply
关键字引入了类型别名,顾名思义,该别名只是该类型的一个方便的名称,而不是其本身的新类型。
您有两个选择,都不是您想要的100%。
您可以按照自己的意愿查看type
中所有可能的值kind
:
LoginReply
或者您可以为function doStuff(o: Reply) {
switch(o.kind)
{
case 'alreadyLoggedIn' :
case 'loginSucceeded' :
o; /* is LoginReply */ break;
case 'logoutReply': o // o is LogoutReply
}
}
子类型添加一个额外的subKind
字段:
LoginReply
答案 1 :(得分:1)
对于所有解决方案,都将保留此答案。请更新,如果您还有其他解决方案。
解决方案#1 :如问题所述,请查看kind
是否为"loginSucceeded"
和"alreadyLoggedIn"
之一。
解决方案2 :如Titian Cernicova-Dragomir在his answer中所述,请使用kind
和subKind
。
解决方案#3 :
如果有人争辩说 Solution#2 使LoginSucceeded
和AlreadyLoggedIn
对被歧视的类型了解得太多,那么人们也可以争辩说,即使在一种情况下歧视程度,他们不应该了解kind
。如果有人遵循该论点,则可以实现这样的解决方案:
type Reply = { kind: "loginReply", t: LoginReply } | { kind: "logoutReply", t: LogoutReply }
type LoginReply = { kind: "loginSucceeded", t: LoginSucceeded } | { kind: "alreadyLoggedIn", t: AlreadyLoggedIn }
interface LoginSucceeded {
}
interface AlreadyLoggedIn {
loggedInUsername: string;
}
interface LogoutReply {
logout: boolean
}
function doStuff(o: Reply) {
switch(o.kind)
{
case 'loginReply' : o.t; // is LoginReply
const loginReply = o.t;
switch(loginReply.kind) {
case 'alreadyLoggedIn' : loginReply.t; /* is AlreadyLoggedIn */ break;
case 'loginSucceeded' : loginReply.t; /* is LoginSucceeded */ break;
}
break;
case 'logoutReply': o.t; // LogoutReply
}
}