【问题标题】:How to type es6 generators with flow如何使用流键入 es6 生成器
【发布时间】:2019-05-12 22:19:11
【问题描述】:

我的 react-native 应用程序中有以下(简化的)代码(我认为 react-native 无关紧要,但这是我发现错误的上下文)。

我正在尝试定义mystringgen 的类型以保持flow 快乐,但我找到的唯一解决方案是string|void,如下所示。下面的代码本身不会返回任何流错误,但是一旦我在其他地方使用mystring,我就会收到错误,因为应该是string,而不是void。我玩过各种组合,但没有运气。

我应该如何修改此代码以修复错误?

// @flow
import * as React from 'react';

type State = {
    mystring: string|void,
};

type genType = Generator<string, void, void>;

export default class Example extends React.Component<{}, State> {
    constructor(props: {}) {
        super(props);
        this.gen = this.makeGenerator();
        const { value } = this.gen.next();
        this.state = {
            mystring: value,
        };
    }

    gen: genType;

    * makeGenerator(): genType {
        yield 'somestring';
    }

    render() {
        const { mystring } = this.state;
        return mystring.charAt(0);
    }
}

编辑:

在上面的代码上运行flow 时,最后一行(这是强制流将mystring 视为字符串的示例):

Cannot call mystring.charAt because property charAt is missing in undefined [1].
 [1]  5│     mystring?: string,

【问题讨论】:

  • 如何将state 定义为接口而不是类型并将mystring 定义为可选?我的意思是mystring?: string。我不明白你对 void 的用法
  • this.gen.next() 最后可能会返回 {value: undefined, done: true},这就是 void 类型的来源。 (我不知道如何解决这个问题)
  • @Milore 我用void 覆盖了发电机耗尽并且没有返回的情况,但是您的mystring?: string 更干净。但它仍然不能解决问题,因为变量作为字符串的其他用途会引发flow 错误。你能举个例子说明你所说的接口是什么意思吗?
  • 我通常将组件的 state 和 props 定义为 interface IState {...},但我刚刚阅读了一篇关于与您一样使用 type 的差异的文章,事实证明这几乎是一样的。您可以发布在其他地方使用 mystring 时遇到的确切错误吗?
  • 好的,我不完全理解你的问题。使用 Typescript 意味着您将处理所有变量的定义和使用中的类型,或者您正在谈论的任何内容。显然,在这种情况下,您必须面对 mystring 可能未定义的事实,并且在这种情况下您无法处理它。只需设置您的退货条件,例如return mystring &amp;&amp; mystring.charAt(0);

标签: javascript react-native ecmascript-6 generator flowtype


【解决方案1】:

Generator 的引用类型类型参数如下所示:

interface Generator<+Yield,+Return,-Next>

当您调用next() 时,您会得到这种类型的结果:

type IteratorResult<+Yield,+Return> =
  | { done: true, +value?: Return }
  | { done: false, +value: Yield };

您可以在 done: true 案例中看到 Flow 假设结果中的 value 属性可能不存在。这意味着无论您如何键入生成器,Flow 都假定 value 的类型在这种情况下可能是 void。但在done: false 情况下,Flow 将使用您指定的任何类型作为生成器的Yield 类型。因此,您可以检查done 的值来细化value 的类型,如下所示:

const result = this.gen.next();
if (!result.done) {
  this.state = {
      mystring: result.value,
  };
}

!result.done 的检查将result 的类型细化为{ done: false, +value: Yield },从而为value 提供string 类型,而void 值不可能。

通过该更改,您可以更新状态类型以从 mystring 的类型联合中删除 void

type State = {
    mystring: string,
};

上面GeneratorIteratorResult 的定义来自Flow 的标准类型定义,您可以在此处查看:

https://github.com/facebook/flow/blob/master/lib/core.js

【讨论】:

  • 这有效,因为它删除了mystring: value, 行上的flow 错误,但我使用mystring 的行仍然给我同样的错误。我分配mystring 的方式意味着在不同的函数中,我无权访问result.done,因此我无法以相同的方式优化类型。是flow的限制吗?
  • 我应该说除了检查done 之外,您还需要将状态类型从mystring: string|void 更改为mystring: string。如果您想避免任何类型转换,您还需要使用 mystring 的默认值初始化状态。
  • 太好了,这行得通!我应该自己想出后半部分...有趣的是,如果我使用解构从生成器中获取收益值,例如const { done, value } = this.gen.next(),那么分配给mystring 会导致流程错误,但它的工作方式与您一样'已经写了。感谢您的帮助!
  • @LaurentS 是的,类型缩小不适用于多个变量。如果您在检查done 时将donevalue 保留在一个result 对象中,Flow 会缩小该对象的类型,进而缩小value 属性的类型。如果将donevalue 解构为两个变量,Flow 没有跟踪它们之间连接的机制。
猜你喜欢
  • 2018-12-03
  • 2014-02-28
  • 2021-04-18
  • 2016-02-01
  • 1970-01-01
  • 2016-05-18
  • 2016-09-24
  • 2017-11-20
  • 2017-01-10
相关资源
最近更新 更多