简体   繁体   English

如何为可区分联合的超集制定通用约束

[英]How do I make a generic constraint for a superset of a discriminated union

When defining a generic class Foo<X> , where X is intended to be a discriminated union type, is there a way to express "X must be a superset of the discriminated union Y"?在定义泛型类Foo<X> ,其中 X 旨在成为可区分联合类型,有没有办法表达“X 必须是可区分联合 Y 的超集”?

I have a situation where I am using a discriminated union to represent different action types.我有一种情况,我使用有区别的联合来表示不同的动作类型。 The real-world context of this is an application using Redux, so actions are each different types with different payloads, and the Redux reducer is a function that can accept any of the actions, so I use a discriminated union of the action types to describe the action parameter.这个真实世界的上下文是一个使用 Redux 的应用程序,所以每个动作都是不同类型的,具有不同的有效载荷,而 Redux reducer 是一个可以接受任何动作的函数,所以我使用动作类型的可区分联合来描述动作参数。

In the example below, which resembles my real-world problem, I have an extensible base class which knows how to handle the BaseActionTypes , and I want to be able to pass in ExtendedTypes as the generic parameter在下面的示例中,类似于我的实际问题,我有一个可扩展的基类,它知道如何处理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);

Playground Link 游乐场链接

My code generates the error:我的代码生成错误:

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)

I'm not too clear on why the base ActionDoer can't doAction(w) here.我不太清楚为什么基本的ActionDoer在这里不能doAction(w) I'm trying to add a constraint that says "whatever union of actions is passed in as ActionTypes , it must include at least the set of actions in the union BaseActionTypes , and can possible include other actions. Or, said another way, ActionTypes must be a superset of BaseActionTypes我试图添加一个约束,表示“无论作为ActionTypes传入的任何动作联合,它都必须至少包括联合BaseActionTypes的一组动作,并且可能包括其他动作。或者,换句话说, ActionTypes必须是BaseActionTypes的超集

I think maybe ActionTypes extends BaseActionTypes might not be the correct kind of constraint here for what I want to do?我想也许ActionTypes extends BaseActionTypes可能不是我想要做的正确类型的约束? Initially extends seemed right because the ExtendedActionTypes is an "extension" of the BaseActionTypes , but thinking about this in terms of class inheritance makes me realize that extends is probably not the correct way of doing this.最初extends似乎是正确的,因为ExtendedActionTypesBaseActionTypes的“扩展”,但是从类继承的角度考虑这一点让我意识到extends可能不是这样做的正确方法。 (ie if class A extends class B, then A has all the fields in B, plus more. Whereas that relationship is not true between ExtendedActionTypes and BaseActionTypes . (即,如果类 A 扩展了类 B,则 A 具有 B 中的所有字段,以及更多字段。而ExtendedActionTypesBaseActionTypes之间的关系不正确。

Is there a better way to express a constraint on a discriminated union to say "X must by a superset of Y" for two discriminated union types X and Y?有没有更好的方法来表达对可区分联合的约束,为两个可区分联合类型 X 和 Y 说“X 必须由 Y 的超集”?

How the generic type works here with extend is that we are constraining that our class finally will be containing one or many of possible members from BaseActionTypes .泛型类型在这里与extend是我们限制我们的类最终将包含来自BaseActionTypes一个或多个可能的成员。 For union types extends means - everything assignable to the type, it means it can have the same amount of variants or less, but nothing more.对于联合类型扩展意味着 - 可以分配给类型的所有内容,这意味着它可以具有相同数量或更少的变体,但仅此而已。 You ask how is then your type ExtendedActionTypes assignable if it has one option more.. and really it is not, it is only because you didn't fill the implementation of types, and all three are the same thing as TS is structurally typed language.你问如果你的类型ExtendedActionTypes有一个更多的选项,那么它如何分配..实际上它不是,这只是因为你没有填写类型的实现,这三个都是一样的,因为 TS 是结构类型化语言. If you add to these types and properties you will have an error, check this here .如果您添加到这些类型和属性中,则会出现错误, 请在此处检查

So ExtendedActionTypes is not assignable to BaseActionTypes because it has more options not less.所以ExtendedActionTypes不能分配给BaseActionTypes因为它有更多的选项而不是更少。

Your error though has different reason, as you are trying to set value of type Walk into value which ActionTypes extends BaseActionTypes .你的错误虽然有不同的原因,因为你试图设置类型的值Walk into value which ActionTypes extends BaseActionTypes And it means the ActionTypes is possible type which doesn't include Walk , so you cannot perform such assignment.这意味着ActionTypes是不包含Walk可能类型,因此您无法执行此类分配。 Below the proove:证明如下:

// no error as `Run` extends `BaseActionTypes`
class ExtendedActionDoer extends ActionDoer<Run> {
}

As you can see Walk cannot be assigned to Run .如您所见,无法将Walk分配给Run

One of possible issue fix is removing the constraint from the class at all:可能的问题修复之一是完全从类中删除约束:

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);

Playground link 游乐场链接

We can also keep the generic but requite to have properties, consider:我们还可以保留泛型但需要有属性,请考虑:

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);

Playground link 游乐场链接

Most important part is - doAction(a: ActionType | BaseActionTypes) I am saying whatever you give me there I will take, but there always will be members of BaseActionTypes .最重要的部分是 - doAction(a: ActionType | BaseActionTypes)我是说无论你给我什么,我都会接受,但总会有BaseActionTypes成员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM