Как повторно использовать общие части рекурсивных типов объединения в TypeScript? - PullRequest
5 голосов
/ 04 февраля 2020

TLDR

Это работает ( Детская площадка ):

type SimpleExpression = number | string | AddOperator<SimpleExpression> | PrintOperator<SimpleExpression>;
type ExtendedExpression = number | string | AddOperator<ExtendedExpression> | PrintOperator<ExtendedExpression> | PowerOperator<ExtendedExpression>;

Но извлечение общего подтипа не работает ( Детская площадка ) :

type CommonExpression<E> = number | string | AddOperator<E> | PrintOperator<E>;
type SimpleExpression = CommonExpression<SimpleExpression>;
type ExtendedExpression = CommonExpression<ExtendedExpression> | PowerOperator<ExtendedExpression>;

Есть ли способ обойти это?

Длинное описание

Недавно TypeScript (3.7) получил расширенную поддержку рекурсивных типов, но все же не все возможно.
Есть много вопросов о StackOverflow, которые объясняют рекурсивные типы, но есть один полезный шаблон, для которого я не могу найти решение.

Отправной точкой является дерево выражений, где у каждого оператора есть дочерние элементы, которые являются Выражения. Это работает:

type Expression = number | string | AddOperator | PrintOperator;
interface AddOperator {
  'first': Expression;
  'second': Expression;
}
interface PrintOperator {
  'value': Expression;
}

Теперь мы хотели бы сделать его более обобщенным c, чтобы у нас было SimpleExpression (только с add и print) и ExtendedExpression, которое также поддерживает power оператор. Мы можем сделать это, и это работает ( Playground ):

interface AddOperator<E> {
  'first': E;
  'second': E;
}
interface PrintOperator<E> {
  'value': E;
}
interface PowerOperator<E> {
  'value': E;
  'exp': E;
}

type SimpleExpression = number | string | AddOperator<SimpleExpression> | PrintOperator<SimpleExpression>;

type ExtendedExpression = number | string | AddOperator<ExtendedExpression> | PrintOperator<ExtendedExpression> | PowerOperator<ExtendedExpression>;

Но теперь, если мы хотим выделить общую часть, используя обобщенный c тип объединения, мы потерпим неудачу ( Playground ):

type CommonExpression<E> = number | string | AddOperator<E> | PrintOperator<E>;
type SimpleExpression = CommonExpression<SimpleExpression>;
type ExtendedExpression = CommonExpression<ExtendedExpression> | PowerOperator<ExtendedExpression>;

Ошибки:

Error: Type alias 'SimpleExpression' circularly references itself.
Error: Type alias 'ExtendedExpression' circularly references itself.

Итак, вопрос: есть ли способ определить два различных типа выражения, которые разделяют общее ядро?

Немного предыстории о сценарии использования: мы хотим предоставить SimpleExpression в библиотеке, но позволить клиентскому коду определять дополнительные операторы и регистрировать обработчики для них. Мы бы хотели, чтобы пользователь мог легко определить свой тип ExtendedExpression, не слишком много печатая.

...