优化以在联合中键入别名
Refine to type alias in union
我正在尝试检查联合类型的值是否是联合类型之一。
在下面的代码中,前三种方法 (1-3) 似乎不正确。
检查特定 属性 方法 (4) 似乎有效,但不清楚它已细化为哪种类型。
- (4)(
A
或B
)细化为哪一类?
- 为什么不能将数字或字符串分配给 (5) 和 (6) 中的
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
已被声明为两个对象类型 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
这里的问题是我们混淆了值级别和类型级别。这有点令人困惑,因为流共享它的 type-level typeof
keyword with javascript's value-level typeof
。这是两个不同的东西,不能互操作。我们可以将类型级别的流 typeof
语法视为编译时操作,将值级别的 javascript typeof
语法视为运行时操作。这种区别的重要之处在于,虽然流在编译时具有 一些 值意识,但 javascript 具有 没有 类型意识在运行时。
所以在这个例子中,我们得到一个运行时值 typeof(a1)
并试图弄清楚如何将它与编译时类型 A
进行比较,并使用此比较进行类型优化。这样做的问题是类型细化必须在编译时和运行时都进行操作,但运行时将不知道类型 A
。在这里我们试图弄清楚如何引用 A
因为流没有给我们一个方法来做到这一点:
if (typeof(a1) === 'A') // How can we refer to `A` at runtime? We can't.
这里的另一个问题是 typeof a1
在所有情况下都会 return 'object'
因为这实际上只是常规 javascript 运行时 typeof
关键字,以及 typeof
returns 'object'
用于所有对象实例。
if (a1 instanceof A) {} // (2) Cannot reference type `A` [1] from a value position
这基本上是同一个问题,但在这个例子中更清楚发生了什么。 Flow 准确地告诉我们问题出在哪里,我们试图从值位置而不是类型位置引用类型 (A
)。 instanceof
是运行时检查,因此不能用于与类型进行比较。
(a1: A) // (3) Cannot cast `a1` to `A` because string [1] is incompatible with `A` [2]
这里我们尝试 type cast / assertion,这基本上不起作用,因为 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`
}
(try)
这里我们将 c.member
细化为 existing,但我们对它一无所知,所以它的类型是 mixed
。这意味着我们不能用它做那么多。这里重要的是,c
的类型仍然是 A | B
,改进它的 属性 根本没有改进包含类型。
现在让我们进入真正的问题:
Which would be the correct way to refine to one of the types in a union, where the type is not a [union of] primitive [types] (i.e. is a [union of object types])?
如果目标是在顶层实际细化某物的对象类型,目前只有一种方法可以在流程中做到这一点:对联合体的每个成员使用 disjoint union. A disjoint union relies on a special flag member of object types that must be of a different literal type。 这意味着如果你使用类似 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);
}
(try)
我正在尝试检查联合类型的值是否是联合类型之一。
在下面的代码中,前三种方法 (1-3) 似乎不正确。
检查特定 属性 方法 (4) 似乎有效,但不清楚它已细化为哪种类型。
- (4)(
A
或B
)细化为哪一类? - 为什么不能将数字或字符串分配给 (5) 和 (6) 中的
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
已被声明为两个对象类型 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
这里的问题是我们混淆了值级别和类型级别。这有点令人困惑,因为流共享它的 type-level typeof
keyword with javascript's value-level typeof
。这是两个不同的东西,不能互操作。我们可以将类型级别的流 typeof
语法视为编译时操作,将值级别的 javascript typeof
语法视为运行时操作。这种区别的重要之处在于,虽然流在编译时具有 一些 值意识,但 javascript 具有 没有 类型意识在运行时。
所以在这个例子中,我们得到一个运行时值 typeof(a1)
并试图弄清楚如何将它与编译时类型 A
进行比较,并使用此比较进行类型优化。这样做的问题是类型细化必须在编译时和运行时都进行操作,但运行时将不知道类型 A
。在这里我们试图弄清楚如何引用 A
因为流没有给我们一个方法来做到这一点:
if (typeof(a1) === 'A') // How can we refer to `A` at runtime? We can't.
这里的另一个问题是 typeof a1
在所有情况下都会 return 'object'
因为这实际上只是常规 javascript 运行时 typeof
关键字,以及 typeof
returns 'object'
用于所有对象实例。
if (a1 instanceof A) {} // (2) Cannot reference type `A` [1] from a value position
这基本上是同一个问题,但在这个例子中更清楚发生了什么。 Flow 准确地告诉我们问题出在哪里,我们试图从值位置而不是类型位置引用类型 (A
)。 instanceof
是运行时检查,因此不能用于与类型进行比较。
(a1: A) // (3) Cannot cast `a1` to `A` because string [1] is incompatible with `A` [2]
这里我们尝试 type cast / assertion,这基本上不起作用,因为 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`
}
(try)
这里我们将 c.member
细化为 existing,但我们对它一无所知,所以它的类型是 mixed
。这意味着我们不能用它做那么多。这里重要的是,c
的类型仍然是 A | B
,改进它的 属性 根本没有改进包含类型。
现在让我们进入真正的问题:
Which would be the correct way to refine to one of the types in a union, where the type is not a [union of] primitive [types] (i.e. is a [union of object types])?
如果目标是在顶层实际细化某物的对象类型,目前只有一种方法可以在流程中做到这一点:对联合体的每个成员使用 disjoint union. A disjoint union relies on a special flag member of object types that must be of a different literal type。 这意味着如果你使用类似 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);
}
(try)