【问题标题】:Under @flow strict, what alternatives are there to Object for utility functions?在@flow strict 下,对于实用功能,Object 有哪些替代方案?
【发布时间】:2019-04-18 17:41:20
【问题描述】:

我有兴趣将我的 Flow 代码切换到 strict 类型检查,但我有一些一般处理对象的低级实用函数,例如:

// @flow strict

const hasKey = (o: Object): (string => boolean) =>
  Object.prototype.hasOwnProperty.bind(o);

const union = (os: Array<Object>): Object =>
  os.reduceRight((acc, o) => ({ ...acc, ...o }), {});

由于严格模式下不允许使用 Object 类型,如何为明确应该对任何通用 Object 操作的函数声明类型?

【问题讨论】:

    标签: javascript node.js flowtype strict


    【解决方案1】:

    在这种情况下,您实际上会从更严格的输入中受益匪浅。通过使用Object,您实际上是关闭所有通过这些函数的数据的输入系统,直到它们可以在其他地方显式地重新输入。这意味着您目前正在丢失大量不需要的输入信息。

    这是泛型的教科书案例,记录在 here

    // @flow strict
    const hasKey = <T: {}>(o: T): (string => boolean) =>
      Object.prototype.hasOwnProperty.bind(o);
    
    const union = <T: {}>(objects: Array<T>): T =>
      objects.reduce((acc, o) => ({ ...acc, ...o }), ({}: $Shape<T>));
    

    上面最重要的部分是&lt;T: {}&gt;中的: {}。这些是类型边界。如果泛型是一种说法,“允许用户传递他们想要的任何类型,并将该类型存储在变量中以便我以后可以引用它”,那么type bounds 表示“允许用户传递他们想要的任何类型,只要该类型是 X 类型的成员。

    由于width subtyping 的工作方式,{} 是最通用的对象类型。实际上所有对象都是{} 的子类型。所以&lt;T: {}&gt; 基本上意味着,“T 应该是任何类型的对象。”

    请注意,这与&lt;T: Object&gt; 非常不同,&lt;T: Object&gt; 基本上意味着“T 是一个对象,从现在开始我不会检查它的任何其他内容。”这意味着我们可以执行以下操作:

    const o: Object = {};
    console.log(o.some.member.that.doesnt.exist); // no error at compile time,
                                                  // but obvious error at runtime
    

    不同于:

    const o: {} = {};
    console.log(o.member); // Error, we don't know about this member property!
    

    因此,通过告诉 flow 参数是 {} 的子类型,我们告诉它它具有对象的基本 API。它有属性,它可以被休息和传播,它可以被字符串索引等等,但没有别的了。此外,通过将数据类型存储为通用 T 并返回该类型,我们正在维护参数的类型信息。这意味着无论我们作为参数传入什么,我们都会从另一边得到相同类型的东西(而不是一个神秘的黑匣子)。

    【讨论】:

    • 我不知道泛型中的类型界限!使用单个参数调用reduceRight() 并在传递的函数中移动空的初始对象是否有任何实质性的作用?我注意到它将推断的返回类型从{||}(Flow 出错)更改为T | {}(当 T 被限定为 {} 时与 T 相同,所以我不知道为什么 Flow 越来越那也错了)。
    • 原来你不能把第二个参数放到reduce(),因为那样你会得到“(node:51258) UnhandledPromiseRejectionWarning: TypeError: Reduce of empty array with no initial value”数组。
    • 哦,是的,这会引起问题。
    • 我更新了答案。我理解为什么使用{} 作为默认值本身会导致错误,但老实说,我无法真正解释为什么将其转换为$Shape&lt;T&gt; 可以解决错误。但是,据我所知,这适用于所有情况。
    • 很抱歉之前没有接受这个答案。我最终切换到 TypeScript。 >:-)
    【解决方案2】:

    也许您可以使用流泛型来编写函数。例如,

    function union <T>(os: Array<T>): T { 
       ...
    }
    

    文档:https://flow.org/en/docs/types/generics/

    【讨论】:

    • 仅仅使用泛型还不够好,因为根据类型系统,您可以传入非对象原语。
    • @NicholasShanks 你是对的,这是一种非常幼稚的方法。购买许多在流程中定义良好的运算符和原生函数。例如,对于“...”运算符只允许对象类型。例如,const x = {...1} 给出 Error:(68, 11) 对象字面量。预期对象而不是数字。因此,此函数仅适用于可与扩展运算符一起使用的数据类型
    猜你喜欢
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多