[英]Refine to type alias in union
我正在尝试检查联合类型的值是否是联合类型之一。
在下面的代码中,前三种方法 (1-3) 似乎不是正确的方法。
检查特定属性的方法 (4) 似乎有效,尽管尚不清楚它已改进为哪种类型。
A
或B
)中精炼成哪种类型?a1.a
?type A = {
a: number
}
type B = {
a: string
}
const a1: A | B = {a: 1}
if (typeof(a1) === 'A') {} // (1) Cannot compare the result of `typeof` to string literal `A` because it is not a valid `typeof` return value
if (a1 instanceof A) {} // (2) Cannot reference type `A` [1] from a value position
(a1: A) // (3) Cannot cast `a1` to `A` because string [1] is incompatible with `A` [2]
if (a1.a) { // (4) Works, but inconclusive?
a1.a = 2 // (5) Cannot assign `2` to `a1.a` because number [1] is incompatible with string [2]
a1.a = '2' // (6) Cannot assign `'2'` to `a1.a` because string [1] is incompatible with number [2]
}
首先,我将通过这段代码来澄清发生了什么:
type A = {
a: number
}
type B = {
a: string
}
const a1: A | B = {a: 1}
常量a1
已被声明为是两种 object 类型A
和B
的联合类型。
if (typeof(a1) === 'A') {} // (1) Cannot compare the result of `typeof` to string literal `A` because it is not a valid `typeof` return value
这里的问题是我们混淆了值级别和类型级别。 这有点令人困惑,因为 flow 与javascript 的 value-level typeof
共享其类型级别的typeof
关键字。 这是两个不同的东西,不能互操作。 我们可以将类型级流typeof
语法视为编译时操作,将值级typeof
语法视为运行时操作。 这种区别的重要一点是,虽然流在编译时对值有一些认识,但 javascript 在运行时对类型没有认识。
所以在这个例子中,我们得到了一个运行时值typeof(a1)
并试图弄清楚如何将它与编译时类型A
进行比较,并将这个比较用于类型细化。 这样做的问题是类型细化必须在编译时和运行时都运行,但运行时将不知道类型A
。 在这里,我们试图弄清楚如何引用A
,因为 flow 没有给我们提供这样做的方法:
if (typeof(a1) === 'A') // How can we refer to `A` at runtime? We can't.
这里的另一个问题是typeof a1
在所有情况下都将返回'object'
,因为这实际上只是常规的 javascript 运行时typeof
关键字,并且typeof
为所有 object 实例返回'object'
。
if (a1 instanceof A) {} // (2) Cannot reference type `A` [1] from a value position
这基本上是相同的问题,但更清楚的是在这个例子中发生了什么。 Flow 准确地告诉我们问题出在哪里,我们试图从值 position 中引用类型 ( A
),而不是类型 position。 instanceof
是运行时检查,因此不能用于与类型进行比较。
(a1: A) // (3) Cannot cast `a1` to `A` because string [1] is incompatible with `A` [2]
在这里,我们正在尝试一种类型转换/断言,它基本上不起作用,因为a1
尚未被细化为A
,并且据流知道它仍然可能是B
,不处理是不安全的那个案子。
if (a1.a) { // (4) Works, but inconclusive?
a1.a = 2 // (5) Cannot assign `2` to `a1.a` because number [1] is incompatible with string [2]
a1.a = '2' // (6) Cannot assign `'2'` to `a1.a` because string [1] is incompatible with number [2]
}
这实际上是一个有效的细化操作,但它只将属性a1.a
细化为存在。 例如我们可以这样做:
type A = {
member: string,
};
type B = {
};
declare var c: A | B;
if (c.member) {
console.log(c.member); // c.member is of type `mixed`
}
( 尝试)
在这里,我们将c.member
细化为现有,但我们对它一无所知,因此它的类型是mixed
的。 这意味着我们不能用它做那么多。 这里重要的是, c
的类型仍然是A | B
A | B
,提炼它的属性根本没有提炼包含类型。
现在让我们来看看真正的问题:
哪一种是细化为联合中的一种类型的正确方法,其中类型不是原始 [类型] 的 [联合](即 [object 类型的联合])?
如果目标是在顶层实际细化 object 类型的东西,那么目前只有一种方法可以在流程中做到这一点:使用不相交的联合。 不相交的联合依赖于 object 类型的特殊标志成员,对于联合的每个成员,该成员必须是不同的文字类型。 这意味着如果你使用类似string
的东西,它不会被识别为不相交的联合:
type A = {
// We're calling this `type` but it could have any name as long
// as it's the same among each member of the union.
type: 'a', // This must be a literal type.
aMember: string,
};
type B = {
type: 'b', // This must be a different literal type.
bMember: number,
};
declare var c: A | B;
if (c.type === 'a') {
// Within this branch, type of `c` is `A`.
(c.aMember: string);
// $FlowFixMe
(c.bMember: number);
} else {
// Within this branch, type of `c` is `B`.
(c.bMember: number);
// $FlowFixMe
(c.aMember: string);
}
( 尝试)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.