真实检查后访问可选道具的流程错误

flow error accessing optional prop after truthy check

流 0.67.1(但行为在 0.73.1 中继续存在)

示例:

type PropOptional = {
  prop?: ComplexType
};                         

type ComplexType = {
  callable: () => void,
  anotherCallable: () => void
};

function usePropOptional(arg1: PropOptional) {
  if (arg1.prop) {
    arg1.prop.callable();
    arg1.prop.anotherCallable();
    arg1.prop.callable();
  }
};

该函数在访问 arg1.prop 上的任何属性之前检查是否存在 arg1.prop。这应该足以验证 arg1.prop 是否已定义。

第一次访问 arg1.prop 属性 时流程很好,这是对 if 块内第一行的 arg1.prop.callable() 的调用。但是,流程会在后续尝试访问完全相同的 if 块中的 arg1.prop 属性时生成错误:

arg1.prop.anotherCallable();
arg1.prop.callable();

我不得不在每一行前面加上死记硬背的 arg1.prop && 真实性检查,或者将 arg1.prop 重新分配给 if 块内的局部变量:

function usePropOptional(arg1: PropOptional) {
  if (arg1.prop) {
    const reallyExists = arg1.prop;
    reallyExists.callable();
    reallyExists.anotherCallable();
    reallyExists.callable();
  }
};

这感觉不对。我做错了什么或遗漏了什么?

您可以在 flow repl here on flow.org.

中查看

这在 FlowType 的 Type Refinement 部分中有记录:

Refinement Invalidations

It is also possible to invalidate refinements, for example:

// @flow
function otherMethod() { /* ... */ }

function method(value: { prop?: string }) {
  if (value.prop) {
    otherMethod();
    // $ExpectError
    value.prop.charAt(0);
  }
}

The reason for this is that we don’t know that otherMethod() hasn’t done something to our value. ...

There’s a straightforward way to get around this. Store the value before calling another method and use the stored value instead. This way you can prevent the refinement from invalidating.

// @flow
function otherMethod() { /* ... */ }

function method(value: { prop?: string }) {
  if (value.prop) {
    var prop = value.prop;
    otherMethod();
    prop.charAt(0);
  }
}

因此,您最后一个案例中的解决方法似乎是避免此问题的建议方法。