Zby*_*byl 7 typescript typescript-generics
这有效(游乐场):
type SimpleExpression = number | string | AddOperator<SimpleExpression> | PrintOperator<SimpleExpression>;
type ExtendedExpression = number | string | AddOperator<ExtendedExpression> | PrintOperator<ExtendedExpression> | PowerOperator<ExtendedExpression>;
Run Code Online (Sandbox Code Playgroud)
但是提取公共子类型不起作用(Playground):
type CommonExpression<E> = number | string | AddOperator<E> | PrintOperator<E>;
type SimpleExpression = CommonExpression<SimpleExpression>;
type ExtendedExpression = CommonExpression<ExtendedExpression> | PowerOperator<ExtendedExpression>;
Run Code Online (Sandbox Code Playgroud)
有什么办法可以解决吗?
最近 TypeScript (3.7) 得到了对递归类型的扩展支持,但仍然不是一切皆有可能。
StackOverflow 上有很多解释递归类型的问题,但有一种有用的模式我找不到解决方案。
起点是一个表达式树,其中每个运算符都有作为表达式的子项。这有效:
type Expression = number | string | AddOperator | PrintOperator;
interface AddOperator {
'first': Expression;
'second': Expression;
}
interface PrintOperator {
'value': Expression;
}
Run Code Online (Sandbox Code Playgroud)
现在我们想让它更通用,这样我们就有 SimpleExpression(只有add和print),以及也支持power运算符的ExtendedExpression 。我们可以这样做,并且它有效(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>;
Run Code Online (Sandbox Code Playgroud)
但是现在如果我们想使用通用联合类型来分解公共部分,我们将失败(Playground):
type CommonExpression<E> = number | string | AddOperator<E> | PrintOperator<E>;
type SimpleExpression = CommonExpression<SimpleExpression>;
type ExtendedExpression = CommonExpression<ExtendedExpression> | PowerOperator<ExtendedExpression>;
Run Code Online (Sandbox Code Playgroud)
错误是:
Error: Type alias 'SimpleExpression' circularly references itself.
Error: Type alias 'ExtendedExpression' circularly references itself.
Run Code Online (Sandbox Code Playgroud)
所以问题是:有没有办法定义两种不同的表达式类型,它们共享共同的核心?
关于使用场景的一些背景知识:我们希望
SimpleExpression在一个库中提供 ,但允许客户端代码定义额外的操作符,并为这些操作符注册处理程序。我们希望用户能够轻松定义他的ExtendedExpression类型,而无需过多键入。
当你使用
type SimpleExpression = number | string | AddOperator<SimpleExpression> | PrintOperator<SimpleExpression>;
type ExtendedExpression = number | string | AddOperator<ExtendedExpression> | PrintOperator<ExtendedExpression> | PowerOperator<ExtendedExpression>;
Run Code Online (Sandbox Code Playgroud)
至少有一条递归应该停止的路径。对于 的情况const simple: SimpleExpression = { value: { value: 5 } };,其类型为PrintOperator<PrintOperator<number>>。
但如果你使用
type CommonExpression<E> = number | string | AddOperator<E> | PrintOperator<E>;
type SimpleExpression = CommonExpression<SimpleExpression>;
type ExtendedExpression = CommonExpression<ExtendedExpression> | PowerOperator<ExtendedExpression>;
Run Code Online (Sandbox Code Playgroud)
递归永远不能停止。事实上,递归本质上发生在类型声明本身中。SimpleExpression类型为CommonExpression<SimpleExpression>,但会扩展为CommonExpression<CommonExpression<SimpleExpression>>,然后CommonExpression<CommonExpression<CommonExpression<SimpleExpression>>>,依此类推,形成无限递归。这就是 TypeScript 不接受这种类型设置的原因。
然而有趣的是,
type SimpleExpression = AddOperator<SimpleExpression> | PrintOperator<SimpleExpression>;
Run Code Online (Sandbox Code Playgroud)
仍然被接受,但没有变量满足这种类型。