简洁地告诉 Flow 可空属性在 属性 访问链中不会为空的惯用方法是什么?

What is the idiomatic way to succinctly tell Flow that nullable properties will not be null in a chain of property accesses?

假设您有几个具有可选属性的简单 Flow 类型:

type A = { b?: B };
type B = { action?: () => void };

并且您想访问链中的属性并知道它们已定义:

a.b.action()

告诉 Flow a.bb.action 安全的惯用方式是什么?

你可以测试它们是否存在,比如a.b && a.b.action && a.b.action()

https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN5gBGA-AFxgBCYAvgNya4HNmUCGAYwwBLOADt6YABQBKUgD4wANzjCAJqw6ooAVzFDRYsFCl8GhOeVRgwfAHRU7gkeNkcW6XfpdGA5qfNLa1sHMAAyMJDHZ0NwyPtog1cZdyA

没有简单的答案。你基本上有三个选择。

  • 绕过类型检查器,放弃类型安全。
  • 为了维护类型安全,请进行运行时检查。 Flow 了解许多运行时检查并将基于它们改进类型。
  • 重构您的程序,使这些属性不再是可选的。

要完全绕过类型检查器并放弃安全性,您可以做类似 (a: any).b.action() 的事情。我不推荐这个。

显然,此问题中没有足够的信息来确定重构您的程序以避免具有可选属性是否可行或什至是可取的。

因此,为了维护类型安全,您需要进行运行时检查。你可以这样做:

if (a.b != null && a.b.action != null) {
  a.b.action();
} else {
  // throw error or something
}

或者,如果您只是想断言它们是非空的,Flow 有专门的函数名为 invariant 用于此目的(当然,您需要弄清楚如何在运行时获取它. 在 Node 中,你可以做到 import invariant from 'assert'。不过,如果你愿意,自己编写也很简单。

invariant(a.b != null && a.b.action != null);
a.b.action();

关于这类事情的一个警告是,如果 Flow 认为可以更改某些内容,它会积极地使类型优化无效。因此,如果在测试和使用之间有任何干预函数调用,它可能会再次开始出错。在这种情况下,您必须将每一位都拉出到一个单独的变量中,例如:

const b = a.b;
invariant(b != null);
const action = b.action;
invariant(action != null);
// other stuff that would have invalidated the type refinement
action();

这是最好的方法。

export default function requireNotNull<T>(value: T, message?: string): $NonMaybeType<T> {
  if (value !== null && value !== undefined) return value;
  throw new Error(message || "Value is null or undefined");
}