gpt4 book ai didi

javascript - 流联合类型在递归调用中不起作用

转载 作者:行者123 更新时间:2023-11-30 20:17:22 24 4
gpt4 key购买 nike

我在对允许 Function 的自定义联合类型进行类型检查时遇到问题, PromiseArray包含 falsePlugin 的实例类。

代码无需类型检查即可运行,并允许开发人员嵌套 Pluginfalse然而,在任何其他允许的类型中,我似乎无法让类型系统允许代码没有任何错误。

我假设我们可以使用递归类型,例如 type T = false | Array<T>所以类型可以是 false 或包含另一个 T 的数组类型。我在 github 评论中读到递归类型或允许,但是找不到代码示例。

// @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 来防止错误然而,对于联合类型,类型检查似乎不再有效。

type Plugins = 
false
| Plugin
| Plugins
| Array<Plugins>
| Promise<Plugins>
| () => Plugins

https://flow.org/try/#example

感谢您的帮助

最佳答案

优先级问题

如果您 run your type definition through 就可以发现一个错误Prettier代码格式化程序:

type Plugins =
| false
| Plugin
| (() => Plugins | Array<Plugins> | Promise<Plugins>)

如您所见,您的顶级 Plugins 类型不接受 ArrayPromise,因为返回值 Plugins 比函数 () => Plugins 具有更高的优先级。你可以fix that by adding parentheses围绕你的 () => Plugins 函数类型:

type Plugins =
| false
| Plugin
| (() => Plugins)
| Array<Plugins>
| Promise<Plugins>

对象构造导致 any

的流程错误

这是向前迈出的一步,但是当您 apply this change to your original code ,您会看到 .pipe(true) 现在无法引发所需的错误。使用 Try Flow 的特性来识别光标下元素的类型,我通过 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 抛出错误,即使它们应该抛出错误也是如此。

您可以通过手动注释构建的引擎作为引擎来解决这个问题,使用类型注释或变量赋值:

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 ,您还可以通过将 Engineconstructor 显式注释为返回 void 来解决此问题:

class Engine {
// …
constructor(params: object): void {
this.params = params
}
// …
}

如果这样做,您就不必在构造Engine 对象的每个地方都添加Engine 注释。

最终代码

结合这两个修复程序以获得最终的工作代码 ( in Try Flow ):

// @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 = {}

// type the constructor as returning `void` to work around
// https://github.com/facebook/flow/issues/6738
constructor(params: object): void {
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
}
}

console.log(
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
// $ExpectError
.pipe(true)
// The following deeply-nested parameter now works
.pipe([() => Promise.resolve([() => [Promise.resolve([new Plugin('plugin-five')])]])])
)

关于javascript - 流联合类型在递归调用中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51796366/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com