繁体   English   中英

细化以在联合中键入别名

[英]Refine to type alias in union

我正在尝试检查联合类型的值是否是联合类型之一。

在下面的代码中,前三种方法 (1-3) 似乎不是正确的方法。

检查特定属性的方法 (4) 似乎有效,尽管尚不清楚它已改进为哪种类型。

  1. 它在(4)( AB )中精炼成哪种类型?
  2. 为什么不能将数字或字符串分配给 (5) 和 (6) 中的a1.a
  3. 哪一种是细化为联合中的一种类型的正确方法,其中类型不是原始类型(即类型别名)?
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 类型AB的联合类型。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM