【问题标题】:How can Flow be forced to cast a value to another type?如何强制 Flow 将值转换为另一种类型?
【发布时间】:2017-05-10 18:28:41
【问题描述】:

是否可以在Flow中强制强制转换变量?

type StringOrNumber = string | number
const foo: StringOrNumber = 'hello'

// I look for something like `const bar:string = (string) foo`
const bar: string = foo // fails
const bar: string = (foo: string) // also fails

【问题讨论】:

    标签: casting flowtype


    【解决方案1】:

    Flow 不会直接从一种类型转换为另一种类型,但您可以执行类似的操作

    const bar: string = (foo: any);
    

    所以您将foo 转换为any,因为any 接受任何类型的值作为输入。然后因为any 类型还允许您从中读取所有可能的类型,您可以将any 值分配给bar,因为any 也是string

    【讨论】:

      【解决方案2】:

      在您给出的示例中,您正在查看从联合类型到其成员之一的“强制转换”。虽然通常将其视为强制转换,但它与其他语言中的类型转换不同。

      通过将foo 的类型设置为string | number,我们告诉Flow 该值可以是字符串或数字。然后我们碰巧在其中放入了一个字符串,但 Flow 并没有因此而放弃我们对其类型的直接断言,即使在以后无法更改的情况下(例如这种情况)也是如此。

      要将其分配给 string 类型的变量,Flow 需要知道即使它可能是 stringnumber,在我们进行分配时,我们确信它只能成为string

      这种减少可能选项的过程称为类型细化

      类型优化

      要细化类型,我们需要证明它一定是我们所说的类型,以 Flow 理解的方式

      在原始示例中,您可以使用typeof

      type StringOrNumber = string | number
      const foo: StringOrNumber = 'hello'
      
      // This would raise an error here:
      // const bar: string = foo
      
      if (typeof foo === "string") {
        // Flow now knows that foo must be a string, and allows this.
        const bar: string = foo
      }
      

      Flow 无法理解人类可以看到的类型细化的所有内容,因此有时您需要查看 the refinement docs 以了解什么可能使 Flow 理解它。

      抑制 cmets

      有时无法表达对 Flow 的改进的安全性。我们可以通过使用suppression comment 来强制 Flow 接受一个语句,这将抑制 Flow 否则会报告的错误。默认抑制注释为$FlowFixMe,但可以配置为不同的注释或cmets。

      Flow会在第二行报错,报告unionValue可能是'number'类型:

      const unionValue: StringOrNumber = 'seven'
      const stringValue: string = unionValue
      

      但是,通过使用抑制注释,这会通过 Flow:

      const unionValue: StringOrNumber = 'seven'
      // $FlowFixMe: We can plainly see this is a string!
      const stringValue: string = unionValue
      

      抑制 cmets 的一个有用功能是,一个没有跟随错误的抑制注释被认为是一个错误。如果我们改变示例中的类型:

      const unionValue: string = 'seven'
      // $FlowFixMe: Even though this is a string, suppress it
      const stringValue: string = unionValue
      

      现在 Flow 会改为报告“未使用的抑制”错误,提醒我们。这在 Flow 应该能够识别细化但不能识别时特别有用 - 通过使用抑制注释,如果未来版本的 Flow 将代码识别为类型安全。

      任意投射

      如果你真的不能以一种可以证明其安全流动的方式来表达它,并且你不能(或不会)使用抑制注释,你可以将任何类型转换为 any 和 @987654336 @任何类型:

      const unionValue: StringOrNumber = 'seven'
      // Flow will be okay with this:
      const stringValue: string = (unionValue: any)
      

      通过将值转换为any,我们要求 Flow 忘记它所知道的关于值类型的任何信息,并假设我们对它所做的任何事情都必须是正确的。如果我们稍后将其放入类型变量中,Flow 将假定这一定是正确的。

      注意事项

      请务必注意,抑制 cmet 和强制转换 any 都是不安全的。它们完全覆盖 Flow,并且会愉快地执行完全无意义的“强制转换”:

      const notAString: {key: string, key2: number} = {key: 'value', key2: 123}
      // This isn't right, but Flow won't complain:
      const stringValue: string = (notAString: any)
      

      在这个例子中,stringValue 持有来自notAStringobject,但 Flow 确定它是一个字符串。

      为避免这种情况,请尽可能使用 Flow 理解的改进,并避免使用其他不安全的“强制转换”技术。

      【讨论】:

      • $FlowFixMe 是否优于任何演员?还是它们大致相当?
      • @Casebash - 它们大部分是等价的,但是(正如我试图在答案中澄清的那样)抑制评论更具前瞻性。如果 Flow 的后续版本学习如何识别您正在执行的优化,则抑制注释将通知您它不再抑制任何内容。对于如何编辑答案以使其更清晰的任何想法,我将不胜感激!
      【解决方案3】:

      这个答案只是一个建议。在浏览 Event 和 HTMLElement 相关类型检查问题的解决方案时,我遇到了很多调用 instanceof 的守卫。

      为了满足类型检查,我刚刚引入了这个通用守卫并将其命名为cast(这当然不会使其成为强制转换),否则我的代码会变得如此臃肿。

      成本当然是性能(在编写游戏时非常相关,但我想大多数用例从类型保护中受益更多,而不是每次迭代的毫秒数)。

      const cast = (type : any, target : any) => {
          if (!(target instanceof type)) {
              throw new Error(`${target} is not a ${type}`);
          }
          return target;
      }
      

      用法:

      const fooLayer = cast(HTMLCanvasElement, document.getElementById("foo-layer"));
      window.addEventListener("click", (ev : Event) =>
        console.log(cast(MouseEvent, ev).clientX - cast(HTMLElement, ev.target).offsetLeft,
          cast(MouseEvent, ev).clientY - cast(HTMLElement, ev.target).offsetTop));
      

      【讨论】:

        【解决方案4】:

        也许是对一个或多个现有答案的更新:目前有一种相当不错的方法可以明确告诉 Flow 不要担心它认为不好的演员表。

        你可以给一个带有特定错误代码的错误抑制指令,像这样:

        type StringOrNumber = string | number
        const foo: StringOrNumber = 'hello'
        
        // I look for something like `const bar:string = (string) foo`
        // const bar: string = foo // would fail
        // $FlowExpectedError[incompatible-cast]
        const baz: string = (foo: string) // no longer fails!!
        

        作为额外的奖励,这是另一种执行演员的方式,而不会受到 Flow 的任何抱怨:

        /*:: if (typeof foo !== "string") throw null; */
        const baz: string = (foo: string) // does not fail!!
        

        这里特殊的/*:: 语法告诉Flow 它应该将注释中的文本(冒号除外)视为普通的JavaScript 代码。我从文档中收集到,此功能的存在是为了提供一种方法来使用特定于 Flow 的语法来装饰您的代码,而不会阻止它也作为纯 JavaScript 运行 - 这具有允许在没有任何 Flow-syntax-stripping 的情况下使用 Flow 的适度好处像 Babel 之类的工具。但是该功能也可以很好地用作表达只有 Flow 可以读取的“断言”的机制。这有点hacky,但我个人认为它足够干净,可以在严肃的项目中使用!我的意思是,只要你只是偶尔做一次......

        当然,您可以将 typeof 检查留在实际运行的实际 JavaScript 中,但这可能会产生我想象的运行时成本?另一方面,这肯定不会。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多