这是一个简单的有界多态性示例,它并不像我期望的那样工作:
// @flow
function thisBreaks<T: 'a' | 'b'>(x: T): T {
if (x === 'a') {
return 'a'
} else {
return 'b'
}
}
function thisWorks<T: 'a' | 'b'>(x: T): T {
return x
}
const a = 'a'
const aPrime: 'a' = thisWorks(a)
const b = 'b'
const bPrime: 'b' = thisWorks(b)
5: return 'a'
^ string. This type is incompatible with the expected return type of
3: function thisBreaks<T: 'a' | 'b'>(x: T): T {
^ some incompatible instantiation of `T`
7: return 'b'
^ string. This type is incompatible with the expected return type of
3: function thisBreaks<T: 'a' | 'b'>(x: T): T {
^ some incompatible instantiation of `T`
我原本期望第一个例子可以工作,例如x === 'a'
检查可以将T
改进为'a'
,对吗?
答案 0 :(得分:2)
这是不可能的,也不可能。这是一个显示原因的例子:
function test<T: number | string>(x: T): T {
if (typeof x === 'number') {
return 1;
} else {
return 'b'
}
}
test((5: 5));
该函数应返回5
类型的值,但返回1
。
那么,会发生什么?我们有一些未知类型T
,我们知道T
&lt ;: string | number
(T
是string | number
的子类型)。在我们优化x
之后,我们知道T
是number
的子类型。这并不意味着T
number
。它可以是5
,如示例中所示,或1 | 2 | 3
。知道T
是number
的子类型并不足以创建T
的值。要做到这一点,我们需要知道T
的下限,但没有办法知道它。
最后一个问题是:为什么你的例子显然是安全的呢?这很简单:如果T
&lt ;: 'a'
,那么它只能是'a'
(或empty
,但它并不重要) 。 'a'
没有其他子类型。所以,从理论上讲,Flow可以支持这一点,但它不太实用:如果你知道x
是'a'
,那么你可以返回x