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

时间:2018-08-11 04:05:13

标签: javascript flowtype union-types

我在类型检查自定义联合类型的流程上遇到麻烦,该联合类型允许包含Function或{实例的PromiseArrayfalse {1}}类。

代码无需类型检查即可工作,并且允许开发人员将PluginPlugin嵌套在任何其他允许的类型内,但是,我似乎无法获得类型系统来允许没有任何代码的代码错误。

我正在假设我们可以使用递归类型,例如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

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

感谢您的帮助

1 个答案:

答案 0 :(得分:1)

优先权问题

如果您run your type definition through代码Prettier会发现一个错误:

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

如您所见,您的顶级Plugins类型将不接受ArrayPromise,因为返回值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