Как сделать ограничение generic c для надмножества дискриминационного объединения - PullRequest
0 голосов
/ 03 марта 2020

При определении обобщенного c класса Foo<X>, где X предназначен для распознаваемого типа объединения, существует ли способ express "X должен быть надмножеством различаемого объединения Y"?

У меня есть ситуация, когда я использую дискриминационный союз для представления различных типов действий. В реальном контексте это приложение, использующее Redux, поэтому у каждого действия есть разные типы с разными полезными нагрузками, а редуктор Redux - это функция, которая может принимать любое из действий, поэтому я использую различаемое объединение типов действий для описания параметр действия.

В приведенном ниже примере, который напоминает мою реальную проблему, у меня есть расширяемый базовый класс, который знает, как обрабатывать BaseActionTypes, и я хочу иметь возможность передать ExtendedTypes как общий параметр c



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

Мой код выдает ошибку:

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.

Есть ли лучший способ express ограничение на дискриминируемое объединение, чтобы сказать "X должен быть надмножеством Y "для двух различающихся типов объединений X и Y?

1 Ответ

1 голос
/ 03 марта 2020

Как работает тип generi c с extend, мы ограничиваемся тем, что наш класс, наконец, будет содержать один или несколько возможных членов из BaseActionTypes. Для типов объединения расширяются средства - все, что присваивается типу, это означает, что он может иметь одинаковое количество вариантов или меньше, но не более того. Вы спрашиваете, как тогда присваивается ваш тип ExtendedActionTypes, если у него есть еще одна опция ... а на самом деле это не так, это только потому, что вы не заполнили реализацию типов, и все три - это то же самое, что TS структурно типизированный язык. Если вы добавите эти типы и свойства, у вас будет ошибка, проверьте это здесь .

Так что ExtendedActionTypes нельзя назначить на BaseActionTypes, потому что у него больше опций, а не меньше.

Ваша ошибка, хотя и имеет другую причину, поскольку вы пытаетесь установить значение типа Walk в значение, которое 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);

ссылка на игровую площадку

Мы также можем сохранить тип c, но при этом иметь свойства, рассмотрим:

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.

...