[英]How do I make a generic constraint for a superset of a discriminated union
在定义泛型类Foo<X>
,其中 X 旨在成为可区分联合类型,有没有办法表达“X 必须是可区分联合 Y 的超集”?
我有一种情况,我使用有区别的联合来表示不同的动作类型。 这个真实世界的上下文是一个使用 Redux 的应用程序,所以每个动作都是不同类型的,具有不同的有效载荷,而 Redux reducer 是一个可以接受任何动作的函数,所以我使用动作类型的可区分联合来描述动作参数。
在下面的示例中,类似于我的实际问题,我有一个可扩展的基类,它知道如何处理BaseActionTypes
,并且我希望能够将ExtendedTypes
作为泛型参数传入
interface Run {
}
interface Walk {
}
type BaseActionTypes = Run | Walk
interface Jump {
}
type ExtendedActionTypes = BaseActionTypes | Jump;
class ActionDoer<ActionTypes extends BaseActionTypes> {
doAction(a: ActionTypes) {
}
walk() {
const w: Walk = {};
this.doAction(w); // ERROR!
}
}
class ExtendedActionDoer extends ActionDoer<ExtendedActionTypes> {
}
const extendedActionDoer = new ExtendedActionDoer();
const j: Jump = {};
extendedActionDoer.doAction(j);
我的代码生成错误:
Argument of type 'Walk' is not assignable to parameter of type 'ActionTypes'.
'Walk' is assignable to the constraint of type 'ActionTypes', but 'ActionTypes' could be instantiated with a different subtype of constraint 'BaseActionTypes'.(2345)
我不太清楚为什么基本的ActionDoer
在这里不能doAction(w)
。 我试图添加一个约束,表示“无论作为ActionTypes
传入的任何动作联合,它都必须至少包括联合BaseActionTypes
的一组动作,并且可能包括其他动作。或者,换句话说, ActionTypes
必须是BaseActionTypes
的超集
我想也许ActionTypes extends BaseActionTypes
可能不是我想要做的正确类型的约束? 最初extends
似乎是正确的,因为ExtendedActionTypes
是BaseActionTypes
的“扩展”,但是从类继承的角度考虑这一点让我意识到extends
可能不是这样做的正确方法。 (即,如果类 A 扩展了类 B,则 A 具有 B 中的所有字段,以及更多字段。而ExtendedActionTypes
和BaseActionTypes
之间的关系不正确。
有没有更好的方法来表达对可区分联合的约束,为两个可区分联合类型 X 和 Y 说“X 必须由 Y 的超集”?
泛型类型在这里与extend
是我们限制我们的类最终将包含来自BaseActionTypes
一个或多个可能的成员。 对于联合类型扩展意味着 - 可以分配给类型的所有内容,这意味着它可以具有相同数量或更少的变体,但仅此而已。 你问如果你的类型ExtendedActionTypes
有一个更多的选项,那么它如何分配..实际上它不是,这只是因为你没有填写类型的实现,这三个都是一样的,因为 TS 是结构类型化语言. 如果您添加到这些类型和属性中,则会出现错误, 请在此处检查。
所以ExtendedActionTypes
不能分配给BaseActionTypes
因为它有更多的选项而不是更少。
你的错误虽然有不同的原因,因为你试图设置类型的值Walk
into value which ActionTypes extends BaseActionTypes
。 这意味着ActionTypes
是不包含Walk
可能类型,因此您无法执行此类分配。 证明如下:
// no error as `Run` extends `BaseActionTypes`
class ExtendedActionDoer extends ActionDoer<Run> {
}
如您所见,无法将Walk
分配给Run
。
可能的问题修复之一是完全从类中删除约束:
type ExtendedActionTypes = BaseActionTypes | Jump;
class ActionDoer {
doAction<ActionType extends BaseActionTypes>(a: ActionType) {
}
walk() {
const w: Walk = {type: 'Walk'};
this.doAction(w);
}
}
class ExtendedActionDoer extends ActionDoer {
doAction(a: ExtendedActionTypes) {
}
}
const extendedActionDoer = new ExtendedActionDoer();
const j: Jump = {type: 'Jump'};
extendedActionDoer.doAction(j);
我们还可以保留泛型但需要有属性,请考虑:
type ExtendedActionTypes = Jump;
class ActionDoer<ActionType> {
doAction(a: ActionType | BaseActionTypes) {
}
walk() {
const w: Walk = {type: 'Walk'};
this.doAction(w);
}
}
class ExtendedActionDoer extends ActionDoer<ExtendedActionTypes> {
doAction(a: ExtendedActionTypes) {
}
}
const extendedActionDoer = new ExtendedActionDoer();
const j: Jump = {type: 'Jump'};
extendedActionDoer.doAction(j);
最重要的部分是 - doAction(a: ActionType | BaseActionTypes)
我是说无论你给我什么,我都会接受,但总会有BaseActionTypes
成员。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.