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
, не слишком много печатая.