Typescript 使用可选参数为子接口键入Guards

Typescript 使用可选参数为子接口键入Guards,typescript,typescript-typings,Typescript,Typescript Typings,我正在尝试为以下三个接口编写类型保护: export interface DeleteBatchAction { documentReference: firebase.firestore.DocumentReference<any>; } export interface UpdateBatchAction extends DeleteBatchAction { data: firebase.firestore.UpdateData; } export interfac

我正在尝试为以下三个接口编写类型保护:

export interface DeleteBatchAction {
  documentReference: firebase.firestore.DocumentReference<any>;
}

export interface UpdateBatchAction extends DeleteBatchAction {
  data: firebase.firestore.UpdateData;
}

export interface SetBatchAction extends UpdateBatchAction {
  options?: firebase.firestore.SetOptions;
}

export type BatchAction = DeleteBatchAction | UpdateBatchAction | SetBatchAction;
每个接口都扩展了最后一个接口。这里最大的问题是SetBatchAction只有一个可选参数,将其与UpdateBatchAction区分开来

1是否有一种方法来编写类型保护,以便BatchAction类型的变量可以可靠地解析为SetBatchAction与UpdateBatchAction


2我是否需要按特定顺序调用类型保护以确保我拥有正确的类型?UpdateBatchAction类型的对象将通过isDeleteBatchAction类型的保护,如果先选中它?

在TypeScript中表示此类内容的常规方法是使用。这是一种联盟类型,联盟中的每个成员都有一个共同的属性,称为判别式,用于区分联盟中的不同成员。此属性的类型应类似于或。以下是一种将BatchAction类型表示为有区别的联合的方法:

export interface DeleteBatchAction {
  type: "DeleteBatchAction"
  documentReference: firebase.firestore.DocumentReference<any>;
}

export interface UpdateBatchAction {
  type: "UpdateBatchAction";
  documentReference: firebase.firestore.DocumentReference<any>;
  data: firebase.firestore.UpdateData;
}

export interface SetBatchAction {
  type: "SetBatchAction";
  documentReference: firebase.firestore.DocumentReference<any>;
  data: firebase.firestore.UpdateData;
  options?: firebase.firestore.SetOptions;
}

export type BatchAction = DeleteBatchAction | UpdateBatchAction | SetBatchAction;
如果您希望重用接口并使用继承模式,您仍然可以这样做,但是您需要向SetBatchAction添加一个类似类型的属性,并按特定顺序检查类型,以便可靠地区分联合成员。例如,每个SetBatchAction都应该有一个类型为true的SetBatchAction属性。像这样:

export interface DeleteBatchAction {
  documentReference: firebase.firestore.DocumentReference<any>;
}

export interface UpdateBatchAction extends DeleteBatchAction {
  data: firebase.firestore.UpdateData;
}

export interface SetBatchAction extends UpdateBatchAction {
  setBatchAction: true; // add this
  options?: firebase.firestore.SetOptions;
}

export type BatchAction = DeleteBatchAction | UpdateBatchAction | SetBatchAction;
这同样有效,但是这里foo的实现比使用歧视联盟的实现更容易出错,在我看来也更难理解

好吧,希望这会有帮助;祝你好运


那么在运行时,SetBatchAction和UpdateBatchAction可能完全相同?那就没有办法可靠地把他们分开了。您能为每种类型添加一个有区别的字符串文本并放弃继承模式吗?或者至少为SetBatchAction添加一个非可选属性?是的,经过更多的研究,我似乎不得不放弃这种模式。让我感到困扰的是,创建一个包含所有这些属性的统一类型以及一个要区分的“类型”字段,这是可选的&只是让消费逻辑知道“如果设置了类型,那么检查对象上的这些特定字段。”如果您愿意更改模式,那么您可能应该创建一个有区别的并集;如果你愿意,我可以写一个答案来证明。我会很感激的。我不熟悉歧视工会的概念。我也会做我自己的研究,但肯定会很高兴看到你的写作和演示。谢谢。我同意以特定顺序依赖类型检查可能会有问题,因此我认为区分联合类型是最好的方法。我唯一担心的是,当声明一个对象时,我仍然必须手动将类型变量指定为它的一个允许值。有什么方法可以使它成为默认的/有什么流行的策略可以与这种区别类型配对,这样我就不会每次都手动这样做了?或者只是必须付出的代价。
export interface DeleteBatchAction {
  documentReference: firebase.firestore.DocumentReference<any>;
}

export interface UpdateBatchAction extends DeleteBatchAction {
  data: firebase.firestore.UpdateData;
}

export interface SetBatchAction extends UpdateBatchAction {
  setBatchAction: true; // add this
  options?: firebase.firestore.SetOptions;
}

export type BatchAction = DeleteBatchAction | UpdateBatchAction | SetBatchAction;
function foo(x: BatchAction) {
  if ("setBatchAction" in x) {
    x // SetBatchAction
    x.options
    x.data
    x.documentReference
    return 3;
  } else if ("data" in x) {
    x // UpdateBatchAction
    x.data
    x.documentReference
    return 2;
  } else {
    x // DeleteBatchAction
    x.documentReference
    return 1;
  }
}