我在类型检查自定义联合类型的流程上遇到麻烦,该联合类型允许包含Function
或{实例的Promise
,Array
或false
{1}}类。
代码无需类型检查即可工作,并且允许开发人员将Plugin
或Plugin
嵌套在任何其他允许的类型内,但是,我似乎无法获得类型系统来允许没有任何代码的代码错误。
我正在假设我们可以使用递归类型,例如false
,因此该类型可以是false或包含另一个type T = false | Array<T>
类型的数组。我在github注释中读到了递归类型或允许使用递归类型,但是找不到代码示例。
T
// @flow
type object = {}
type Plugins =
false
| Plugin
| () => Plugins
| Array<Plugins>
| Promise<Plugins>
class Plugin {
name: string
constructor(name) {
this.name = name
}
}
class Engine {
params = {}
plugins = {}
constructor(params: object) {
this.params = params
}
pipe(plugin: Plugins): Engine {
// do nothing if plugins is false
if (plugin === false) {
return this
}
else if (typeof plugin === 'function') {
this.pipe(plugin())
}
// get plugin from inside a Promise
else if (plugin instanceof Promise) {
plugin.then(plugin => this.pipe(plugin))
}
// iterate over each item of an array
else if (Array.isArray(plugin)) {
plugin.forEach(plugin => this.pipe(plugin))
}
// initialize each plugin
else if (plugin instanceof Plugin) {
this.plugins[plugin.name] = plugin
}
// return this for chaining
return this
}
}
const engine = new Engine({ name: 'one'})
.pipe(new Plugin('plugin-one'))
.pipe([new Plugin('plugin-two')])
.pipe(Promise.resolve([new Plugin('plugin-three')]))
.pipe(() => new Plugin('plugin-four'))
// should cause a flow type error
.pipe(true)
// I would like this to work deeply nested in any acceptable type
.pipe([() => Promise.resolve([() => [Promise.resolve([new Plugin('plugin-five')])]])])
setTimeout(function() {
console.log(engine)
}, 20)
我尝试了多种不同的类型定义,但均获得了不同程度的成功,但是它们通常可以修复警告,但不再捕获类型错误。
这是试图将最终类型与包装器类型分开
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>
这将通过向联合类型添加type PluginTypes = false | Plugin
type Plugins =
Array<PluginTypes>
| Promise<PluginTypes>
| () => PluginTypes
来防止错误,但是类型检查似乎不再起作用。
Plugins
感谢您的帮助
答案 0 :(得分:1)
如果您run your type definition through代码Prettier会发现一个错误:
type Plugins =
| false
| Plugin
| (() => Plugins | Array<Plugins> | Promise<Plugins>)
如您所见,您的顶级Plugins
类型将不接受Array
或Promise
,因为返回值Plugins
具有更高的 优先于功能() => Plugins
。您可以在() => Plugins
函数类型周围fix that by adding parentheses:
type Plugins =
| false
| Plugin
| (() => Plugins)
| Array<Plugins>
| Promise<Plugins>
any
这是向前迈出的一步,但是当您apply this change to your original code时,您将看到.pipe(true)
现在无法引发期望的错误。通过使用“尝试流”的功能来确定光标下方元素的类型,我发现了为什么在底部附近的adding some test code发生了这种情况:
const initialEngine = new Engine({ name: "one" });
// In Try Flow, put the cursor within `initialEngine`. The type is `any`.
new Engine(…)
返回类型any
的值,而不是您期望的类型Engine
。这就是any
的原因,即使以后应该没有对.pipe
的调用之后也不会引发错误。
您可以通过以下方式解决此问题:使用类型注释或变量赋值,以手动注释作为Engine
的构造引擎:
console.log(
(new Engine({ name: 'one'}): Engine)
.pipe(new Plugin('plugin-one'))
// …
)
const initialEngine: Engine = new Engine({ name: 'one'})
console.log(
initialEngine
.pipe(new Plugin('plugin-one'))
// …
)
理想情况下,您可以使new Engine(…)
返回Engine
,但是我认为这是Flow中的错误,而不是通过更改代码来解决的错误。我的证据表明,删除pipe
方法中的以下代码也可以解决此问题:
// get plugin from inside a Promise
else if (plugin instanceof Promise) {
plugin.then(plugin => this.pipe(plugin))
}
我对此有created an issue on Flow’s bug tracker,用一个更简单的例子演示了这个问题。该错误需要奇怪的特定因素组合才能触发。遇见他们全是你的运气。
在修复该错误之前,您应该使用添加类型注释的解决方法。
正如wchargin提到in the GitHub issue一样,您也可以通过将Engine
的{{1}}显式注释为返回constructor
来解决此问题:
void
如果这样做,则不必在构造class Engine {
// …
constructor(params: object): void {
this.params = params
}
// …
}
对象的每个位置添加一个Engine
注释。
组合这两个修复程序以获得最终的工作代码(in Try Flow):
Engine