【问题标题】:Refine to type alias in union细化以在联合中键入别名
【发布时间】:2021-01-19 09:30:52
【问题描述】:

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

在下面的代码中,前三种方法 (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]
}

【问题讨论】:

  • (4) 不起作用,0"" 都是假的,1"x" 都是真的。
  • 我本来希望 typeof a1.a === "number" 可以工作(真 = A,假 = B),它在 TypeScript 中可以工作,但 Flow doesn't seem to like it... :-|跨度>
  • @T.J.Crowder 很抱歉,我没有意识到 0"" 是假的,1"x" 是真的。你能写出虚假和真实的陈述吗?谢谢
  • (4) 是if (a1.a),但这并不能告诉您a1A 还是B,因为它对于@987654342 都是错误的@(数字)和""(字符串),1(数字)和"1"(字符串)都是如此。也就是说,0"" 都是 falsy 值(在条件中使用时强制转换为 false 的值)。完整的虚假值集是:0""NaNnullundefined,当然还有false(在浏览器上也有document.all...原因)。所有其他值都是truthy
  • 或许这篇文章能帮上忙:stackoverflow.com/questions/51528780/…

标签: javascript functional-programming flowtype


【解决方案1】:

首先,我将通过这段代码来澄清发生了什么:

type A = {
  a: number
}

type B = {
  a: string
}

const a1: A | B = {a: 1}

常量 a1 已被声明为两种对象类型的联合类型,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's value-level typeof 共享其 type-level typeof keyword。这是两个不同的东西,不能互操作。我们可以将类型级流typeof 语法视为编译时操作,将值级javascript 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'

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 改进为现有,但我们对它一无所知,因此它的类型是mixed。这意味着我们不能用它做那么多。这里重要的是,c 的类型仍然是A | B,提炼它的属性根本没有提炼包含类型。

现在让我们来看看真正的问题:

哪一种是精炼为联合中的一种类型的正确方法,其中该类型不是原始 [类型] 的 [联合](即是 [对象类型的联合])?

如果目标实际上是在顶层细化对象类型,目前只有一种方法可以在流程中做到这一点:使用disjoint union。不相交的联合依赖于对象类型的特殊标志成员,对于联合的每个成员,该成员必须是不同的 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多