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"?
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.
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
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);
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. 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
I think maybe ActionTypes extends BaseActionTypes might not be the correct kind of constraint here for what I want to do? 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. (i.e. 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.
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?