flowtype - Flow 如何解释泛型类型?
问题描述
我想了解 Flow 如何决定泛型类型使用什么类型,以及是否有办法控制泛型类型在什么级别被推断(我的意思是在下面进一步解释)。
这个问题的灵感来自How to type a generic function that return subtypes。我认为这两个问题是有区别的,因为这个问题侧重于理解如何
T
选择,而链接的重点是键入函数的返回类型。
恒等函数是一个很好的剖析示例。它的类型相当简单
function identity<T>(value: T): T;
这似乎是足够的信息来了解实现应该是什么。但是,我觉得这种类型不足以知道身份功能实际上是做什么的。例如,我们可以(正如链接的问题试图做的那样),
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
这不会进行类型检查,Flow 会抱怨返回空字符串。但是,我想在许多语言中这会很好——我们在输入 astring
时返回 a string
,否则我们返回value
类型的原始值——T
但由于某种原因 Flow 不喜欢这样。
我的困惑因这个答案而更加复杂,我们可以返回value.substr(0, 0)
而不是空字符串,Flow 将不再抱怨,并且无法返回严格相等的值,
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
我认为造成这种差异的一个主要原因是文字可以像 Flow 中的类型一样,除了“JavaScript 类型”。例如,
const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type
都是有效的。然而,这意味着当我们有一个 type 的函数时T => T
,我们不知道 Flow 是在推断字面量还是 JavaScript 类型作为类型。
我想知道是否有某种方法可以知道 Flow 为函数中的泛型类型推断出什么,或者是否有办法将泛型类型的范围限定为“文字”级别或“JavaScript”级别。有了这种能力,我们可以键入将值强制为该类型的默认值的函数(即,字符串将转到空字符串,数字将转到 0)。这里函数的类型实际上是T => T
,但希望 Flow 可以避免抱怨返回默认值。
解决方案
如果不直接回答问题,希望在这里对发生的事情有所了解。
首先让我们以您的第一个示例为例:
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
函数签名是identity<T>(T): T
. 这基本上是在说:
- 我们正在创建一个新类型
T
,它可以是任何东西 (<T>
)。 - 我们的函数将接收一个 type 的参数
T
。 - 我们的函数将返回一个 type 的值
T
。
从现在开始,这些限制都不会改变,类型T
也不会改变。identity
必须返回 的确切类型T
,而不是其类型的子集。让我们看看为什么。
identity<'some string'>('some string');
在这种情况下, 的类型T
是文字类型,'some string'
。在调用上述函数的情况下,我们会发现typeof value === 'string'
并尝试返回''
a string
。string
但是,是 is 的超类型T
,'some string'
所以我们违反了函数的约定。
在简单字符串的情况下,这一切似乎都是人为的,但它实际上是必要的,并且在扩展到更复杂的类型时更加明显。
让我们看看我们奇怪的身份函数的正确实现:
function identity<T>(value: T): T | string {
if (typeof value === 'string') {
return '';
}
return value;
}
T
只能通过完全匹配的东西来满足返回类型,T
在我们的签名中只能是value
。但是,我们有一个特殊情况,identity
可能会返回 a string
,所以我们的返回类型应该是T | string
(或者,如果我们想要超级具体,T | ''
)的联合。
现在让我们继续看第二个例子:
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
在这种情况下,流只是不支持value === ''
作为细化机制。流程中的细化非常挑剔,我喜欢将其视为在我的代码上运行的一些简单正则表达式的列表。实际上只有一种方法可以将类型细化为字符串,那就是使用typeof value === 'string'
. 其他比较不会细化为字符串。精炼泛型肯定也有一些奇怪的地方,但是像这样的东西可以正常工作(精炼确实如此,它仍然表现出之前与泛型相关的错误,当然):
function identity<T>(value: T): T {
if (typeof value === 'string' && (value: string) === '') {
return '';
}
return value;
}
(试试)
至于这个substr
例子,这对我来说绝对是一个错误。似乎您可以对String
返回 a 的任何方法执行相同的操作string
,例如concat
or slice
。
我想知道是否有某种方法可以知道 Flow 为函数中的泛型类型推断出什么
在函数体流中并没有真正推断出泛型的类型。泛型有一个具体的定义(T
本质T
上是一个未知类型,除非它有边界,在这种情况下,它是一个与这些边界匹配的未知类型)。Flow 可以推断进入函数调用的参数类型,但这与函数的编写方式无关。
或者是否有办法将泛型类型限定为“文字”级别或“JavaScript”级别。有了这种能力,我们可以键入将值强制为该类型的默认值的函数(即,字符串将转到空字符串,数字将转到 0)。这里函数的类型实际上是 T => T,但希望 Flow 可以避免抱怨返回默认值。
这里的问题是,这将不再是T => T
. 正如我上面所展示的,打破这样的实现是微不足道的。
推荐阅读
- checkbox - 如何在材料单元格内添加复选框并使用angular8检索组合记录
- c# - 如何在服务器端处理用户日期并根据用户时间而不是服务器每天发送通知
- html - 书写模式:在 Facebook 应用程序中打开网站时,vertical-rl 不起作用
- google-cloud-platform - 如何使存储桶 b1 的文件夹与谷歌云中的存储桶 b2 保持同步?
- google-bigquery - 使用 Bigquery 存储 API 时出错
- r - R:连续值函数之间的差值之和
- node.js - Nodejs socket.io 服务器选项
- flutter - 仅当 API 响应出现抖动时如何加载 UI?
- c++ - C++ 打印奇怪的行为(潜在的内存泄漏?)
- laravel - DataTable 标头在模态中不能很好地呈现