在 Flow 中输入 "camel caser":方差问题?

Typing a "camel caser" in Flow: variance issues?

try flow link

我一直在输入一个 "camel caser" 函数(一个使用 JSON 和驼峰式大小写它的键的函数)。一路上我 运行 遇到了一些问题,我很好奇你们是否有任何建议。

camel caser 从不改变其参数的形式,所以我想保留我传入的任何类型;理想情况下,在一个数字数组上调用 camelize 会 return 另一个数字数组,等等

我从以下开始:

type JSON = null | string | number | boolean | { [string]: JSON } | JSON[]
function camelize<J: JSON>(json: J): J {
    throw "just typecheck please"
}

这对于简单的情况非常有效,nullstringnumberboolean,但对于 JSON 字典或数组。例如:

const dictionary: { [string]: number } = { key: 123 }
const camelizedDictionary = camelize(dictionary)

将因类型错误而失败。如果您传入一个值,例如键入 number[],则会出现类似的问题。我想我理解这个问题:数组和字典是可变的,因此它们指向的值的类型是不变的;数字数组 不是 JSON[] 的子类型,因此 Flow 会抱怨。如果数组和字典是协变的,我相信这种方法会奏效。

虽然它们不是协变的,但你们对我应该如何考虑这个问题有什么建议吗?

使用属性方差来解决你的字典问题:

type JSON = null | string | number | boolean | { +[string]: JSON } | JSON[]

https://flowtype.org/blog/2016/10/04/Property-Variance.html

至于 Arrays 的问题,正如您指出的那样,问题出在可变性上。不幸的是 Array<number> 不是 Array<JSON> 的子类型。我认为获得所需内容的唯一方法是明确枚举所有允许的 Array 类型:

type JSON = null | string | number | boolean | { +[string]: JSON } | Array<JSON> | Array<number>;

我在这里添加 Array<number> 来说明我的观点。显然这很麻烦,特别是如果您还想包含 JSON 元素的任意组合(如 Array<string | number | null>)。但它有望解决常见问题。

(我也改成我比较熟悉的数组语法,但功能上应该没有区别)

有人说要添加 Array 的只读版本,这类似于协变对象类型,我相信它会解决您的问题。但到目前为止还没有任何结果。

根据您给出的内容完成tryflow

Flow 现在支持 $ReadOnly and $ReadOnlyArray,因此另一种方法是将 JSON 类型定义为

type JSON = 
  | null 
  | string 
  | number 
  | boolean 
  | $ReadOnly<{ [string]: JSON }> 
  | $ReadOnlyArray<JSON>

这解决了上述问题之一,因为 $ReadOnlyArray<number>$ReadOnlyArray<JSON> 的子类型。

根据 camelize 函数的实现,这可能不起作用,因为它可能会将键修改为驼峰式。但是,了解 Flow 中的只读实用程序是件好事,因为它们功能强大并且允许使用更简洁且可能更正确的函数类型。