代码之家  ›  专栏  ›  技术社区  ›  synthet1c

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

  •  1
  • synthet1c  · 技术社区  · 6 年前

    Function , Promise Array false 或一个例子 Plugin 上课。

    插件

    我假设我们可以使用递归类型 type T = false | Array<T> 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

    谢谢你的帮助

    1 回复  |  直到 6 年前
        1
  •  1
  •   Rory O'Kane Erce    6 年前

    优先权问题

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

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

    如你所见,你的高层 Plugins 类型不接受 Array 或者 Promise 插件 有更高的 优先 () => 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) 现在无法抛出所需的错误。使用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(…) 返回类型为的值 任何 而不是类型 Engine 任何 为什么不打电话给 .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'))
        // …
    )
    

    理想情况下你可以 新引擎() 发动机 ,但我认为这是流中的一个bug,而不是通过更改代码来解决的问题。我的证据是 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 ,也可以通过显式地注释来解决此问题 发动机 s公司 constructor 作为回报 void

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

    如果你这样做,你就不必添加 发动机 发动机

    最终准则

    将这两个补丁结合起来,得到最终的工作代码( 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')])]])])
    )