我想了解 Flow 如何决定泛型类型使用什么类型,以及是否有办法控制推断泛型类型的级别(我的意思是进一步解释)。
这个问题的灵感来自How to type a generic function that returns subtypes。我认为这两个问题是有区别的,因为这个问题侧重于理解如何
T选择,而链接则侧重于键入函数的返回类型。
身份函数是一个很好的剖析示例。它的类型相当简单
function identity<T>(value: T): T;
Run Code Online (Sandbox Code Playgroud)
这似乎足以了解实现应该是什么。但是,我觉得这种类型不足以知道身份函数实际上是做什么的。例如,我们可以(正如链接的问题试图做的那样),
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
这不会进行类型检查,Flow 会抱怨返回空字符串。然而,我想在许多语言中这会很好——我们在输入astring时返回 a string,否则我们将返回value类型的原始值——T但出于某种原因 Flow 不喜欢这样。
这个答案让我的困惑更加复杂,我们可以返回value.substr(0, 0)而不是空字符串并且 Flow 将不再抱怨,并且无法返回严格相等的值,
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
我认为造成这种差异的一个主要原因是,除了“JavaScript 类型”之外,文字还可以像 Flow 中的类型一样。例如,
const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type
Run Code Online (Sandbox Code Playgroud)
都是有效的。然而,这意味着当我们有一个 type 的函数时T => T,我们不知道 Flow 是将文字类型还是 JavaScript 类型推断为类型。
我想知道是否有某种方法可以知道 Flow 对函数中的泛型类型推断出什么,或者是否有办法将泛型类型的范围限定在“文字”级别或“JavaScript”级别。有了这种能力,我们就可以键入将值强制为该类型的默认值的函数(即,字符串将变为空字符串,数字将变为 0)。这里函数的类型实际上是T => T,但希望可以防止 Flow 抱怨返回默认值。
如果没有直接回答问题,希望在这里能说明正在发生的事情。
让我们首先举你的第一个例子:
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
函数签名是identity<T>(T): T. 这基本上是说:
T,它可以是任何东西 ( <T>)。T。T。从现在开始,这些限制都不会改变,类型T也不会改变。identity必须返回 的确切类型T,而不是其类型的子集。让我们来看看为什么。
identity<'some string'>('some string');
Run Code Online (Sandbox Code Playgroud)
在这种情况下,类型T是文字类型,'some string'。在调用上述函数的情况下,我们会发现typeof value === 'string'并尝试返回''a string。string但是,是的超类型T是'some string',所以我们侵犯了该函数的合同。
在简单字符串的情况下,这一切似乎相当人为,但实际上是必要的,并且在扩展到更复杂的类型时更加明显。
让我们看看我们奇怪的身份函数的正确实现:
function identity<T>(value: T): T | string {
if (typeof value === 'string') {
return '';
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
的返回类型T只能由完全匹配的东西来满足T,在我们的签名的情况下只能是value。然而,我们有一个特殊的情况,其中identity可能返回 a string,所以我们的返回类型应该是T | string(或者,如果我们想要超级特定,T | '') 的联合。
现在让我们继续第二个例子:
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,流只是不支持value === ''作为一种细化机制。流程中的细化非常挑剔,我喜欢将其视为在我的代码上运行的一些简单正则表达式的列表。实际上只有将类型细化为字符串的方法,那就是使用typeof value === 'string'. 其他比较不会细化到字符串。提炼泛型肯定也有一些奇怪的地方,但像这样的东西工作正常(当然,提炼确实如此,它仍然表现出以前与泛型相关的错误):
function identity<T>(value: T): T {
if (typeof value === 'string' && (value: string) === '') {
return '';
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
(尝试)
至于这个substr例子,对我来说这绝对是一个错误。似乎您可以对任何String返回 a 的方法执行相同的操作string,例如concator slice。
我想知道是否有某种方法可以知道 Flow 对函数中的泛型类型推断出什么
在函数体流中并没有真正推断出泛型的类型。泛型有一个具体的定义(Tis T,本质上是一个未知类型,除非它有边界,在这种情况下,它是一个与这些边界匹配的未知类型)。Flow 可以推断进入函数调用的参数类型,但这应该与函数的编写方式无关。
或者是否有办法将泛型类型的范围限定在“文字”级别或“JavaScript”级别。有了这种能力,我们就可以键入将值强制为该类型的默认值的函数(即,字符串将变为空字符串,数字将变为 0)。这里函数的类型实际上是 T => T,但希望可以防止 Flow 抱怨返回默认值。
这里的问题是这将不再是T => T. 正如我上面所展示的,破坏这样的实现是微不足道的。
| 归档时间: |
|
| 查看次数: |
189 次 |
| 最近记录: |