Flow如何解释通用类型? [英] How does Flow interpret generic types?
问题描述
我想学习Flow如何决定用于泛型的类型,以及是否有一种方法可以控制在哪种级别上推断出泛型(我的意思是进一步解释).
此问题的灵感来自如何键入通用函数返回子类型.我认为这两个问题之间是有区别的,因为这个问题着重于理解如何选择
T
,而链接所关注的是着重于键入函数的返回类型.
身份功能是一个很好的例子.它的类型相当简单
function identity<T>(value: T): T;
这似乎是足够的信息,可以知道实现应该是什么.但是,我觉得这种类型不足以知道身份功能的实际作用.例如,我们可以(链接的问题尝试这样做)
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
此答案使我感到困惑,在这里我们可以返回value.substr(0, 0)
而不是空字符串和Flow将不再抱怨,并且由于无法返回严格相等的值,
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
我认为造成这种差异的主要原因是,除了"JavaScript类型"外,文字还可以像Flow中的类型一样工作.例如, 都有效.但是,这意味着当我们拥有类型为 我想知道是否有某种方法可以了解Flow对函数中的泛型类型的推断,或者是否有一种方法可以将泛型类型的作用域限定为文字"级别或"JavaScript"级别.使用此功能,我们可以键入将值强制转换为该类型的默认值的函数(即,字符串将变为空字符串,数字将变为0).这里的函数类型实际上是 希望在这里阐明所发生的事情,如果不能直接回答问题的话. 让我们首先举一个例子: 功能签名为 从现在开始,这些限制都不会改变,并且 在这种情况下, 在简单字符串的情况下,这一切似乎都是人为的,但这实际上是必要的,并且在扩展为更复杂的类型时更为明显. 让我们看看我们奇怪的身份函数的正确实现: 返回类型 现在让我们继续第二个示例: 在这种情况下,流只是不支持 这里的问题是,它将不再是 I would like to learn how Flow decides what type to use for a generic type, and if there is a way to control at what level the generic type gets inferred (what I mean by this is explained further down). This question is inspired by How to type a generic function that returns subtypes. I think there is a distinction between the two questions because this one focuses on understanding how The identity function is a great example to dissect. Its type is fairly straightforward This seems like enough information to know what the implementation should be. However, I feel like this type is insufficient to know what the identity function actually does. For example, we could have (as the linked question tries to do), This does not typecheck, with Flow complaining about returning the empty string. However, I would imagine in many languages that this would be fine--we are returning a My confusion is compounded by both this answer, where we can return I think a major reason for this discrepancy is that literals can act like types in Flow, in addition to the "JavaScript type". For example, are both valid. However, this means that when we have a function of type I would like to know if there is some way of either knowing what Flow infers for generic types in a function or if there is a way to scope the generic type to be at the "literal" level or "JavaScript" level. With this ability, we could type function that coerces values to the default value for that type (i.e., strings would go to the empty string, numbers would go to 0). Here the type of the function would effectively be Hoping to shed a little light here on what's going on, if not answer the question directly. Let's take your first example first of all: The function signature is From this point forward, none of these restrictions are going to change, and the type of In this case the type of This all seems rather contrived in the case of simple strings, but it's actually necessary, and much more obvious when scaling up to more complex types. Let's look at a proper implementation of our weird identity function: A return type of Now let's move on to this second example: In this case, flow just doesn't support (Try) As for the I would like to know if there is some way of either knowing what Flow infers for generic types in a function Within the function body flow doesn't really infer the type of a generic. A generic has a concrete definition ( or if there is a way to scope the generic type to be at the "literal"
level or "JavaScript" level. With this ability, we could type function
that coerces values to the default value for that type (i.e., strings
would go to the empty string, numbers would go to 0). Here the type of
the function would effectively be T => T, but hopefully Flow could be
prevented from complaining about returning the default values. The problem here is that this would no longer be 这篇关于Flow如何解释通用类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type
T => T
的函数时,我们不知道Flow是否将文字或JavaScript类型作为类型推断.T => T
,但是希望可以防止Flow抱怨返回默认值.function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
identity<T>(T): T
.基本上是这样说的:
T
,它可以是任何类型(<T>
).T
的单个参数.T
的值.T
的类型也不会改变. identity
必须返回T
的 exact 类型,而不是其类型的子集.让我们看看为什么.identity<'some string'>('some string');
T
的类型是文字类型'some string'
.在调用上述函数的情况下,我们会发现typeof value === 'string'
并尝试返回''
,即string
.但是,string
是T
的超类型,它是'some string'
,因此我们违反了该函数的约定.function identity<T>(value: T): T | string {
if (typeof value === 'string') {
return '';
}
return value;
}
T
只能由与T
完全匹配的东西满足,在我们签名的情况下,该值只能为value
.但是,我们有一个特殊情况,其中identity
可能返回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;
}
T => T
.如上所示,破坏这样的实现是微不足道的.
T
is chosen, where as the linked on is focuses on typing the return type of a function.function identity<T>(value: T): T;
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
string
when a string
was inputted, otherwise we are returning the original value
of type T
--but for some reason Flow does not like this.value.substr(0, 0)
instead of the empty string and Flow will no longer complain, and by the inability to return a strictly equal value,function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type
T => T
, we do not know if Flow is inferring the literal or JavaScript type as the type.T => T
, but hopefully Flow could be prevented from complaining about returning the default values.function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
identity<T>(T): T
. This is basically saying:
T
which could be anything (<T>
).T
.T
.T
is also not going to change. identity
must return the exact type of T
, not a subset of its type. Let's look at why.identity<'some string'>('some string');
T
is the literal type, 'some string'
. In the case of this invocation of the above function, we would find that typeof value === 'string'
and attempt to return ''
, a string
. string
, however, is a supertype of T
which is 'some string'
, so we have violated the contract of the function.function identity<T>(value: T): T | string {
if (typeof value === 'string') {
return '';
}
return value;
}
T
can only be satisfied by something which exactly matches T
, which in the case of our signature can only be value
. However, we have a special case where identity
may return a string
, so our return type should be a union of T | string
(or, if we wanted to be super specific, T | ''
).function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
value === ''
as a refinement mechanism. Refinement in flow is very picky, I like to think of it as a list of a few simple regular expressions that are run over my code. There's really only way to refine the type to a string, and that's by using typeof value === 'string'
. Other comparisons won't refine to string. There's definitely also some wonkiness around refining generics, but something like this works fine (the refinement does, it still exhibits the previous generic-related error, of course):function identity<T>(value: T): T {
if (typeof value === 'string' && (value: string) === '') {
return '';
}
return value;
}
substr
example, that definitely looks like a bug to me. It seems you can do the same with any method on String
that returns a string
, such as concat
or slice
.
T
is T
, essentially an unknown type, unless it has bounds, in which case it is an unknown type that matches those bounds). Flow may infer the types of parameters going into invocations of the function, but that should have no bearing on how the functions are written.
T => T
. As I've shown above, breaking such an implementation is trivial.