使用动态函数引用时出现流错误

时间:2018-10-09 17:40:42

标签: javascript react-native flowtype

我在React项目中有一个函数,该函数遍历数组并将每个项目与另一个变量进行比较。现在,数组中的项目可以是字符串或正则表达式。因此,我保留了一个名为compareFn的变量,并根据项目的类型将其引用指向equalpatternMatch。下面是示例代码。

const equal = (privilege: string, whitelistVal: string) => privilege === whitelistVal;
const patternMatch = (privilege: string, whitelistPattern: RegExp) => whitelistPattern.test(privilege);

...

const compareFn = whitelistItem instanceof RegExp ? patternMatch : equal;
if (compareFn(privilegeItem, whitelistItem)) {
  // Do something
}

使用此代码,我在第36行中突出显示whitelistItem时出现了流错误,如下所示:

  

无法调用带有whitelistItem绑定到whitelistPattern的compareFn   因为字符串[1]与RegExp [2]不兼容。

     

35 | const compareFn = whitelistItem RegExp的instance?   patternMatch:相等;
  36 |如果(compareFn(privilegeItem,whitelistItem)){

对我来说,Flow似乎无法识别函数引用的条件分配。感谢您为解决此问题提供的任何帮助。

我正在使用Flow 0.71.0和React Native 0.55.3。

1 个答案:

答案 0 :(得分:0)

这是可行的方法:

const equal = (privilege: string, whitelistVal: string) => privilege === whitelistVal;
const patternMatch = (privilege: string, whitelistPattern: RegExp) => whitelistPattern.test(privilege);


declare function getCompareFn(
  whitelistItem: RegExp
): (string, RegExp) => boolean;
declare function getCompareFn(
  whitelistItem: string
): (string, string) => boolean;

function getCompareFn(whitelistItem) {
  return whitelistItem instanceof RegExp ? patternMatch : equal;
}

const whitelistItem1 = /test/;
const whitelistItem2 = "test";

const privilegeItem = "test";

getCompareFn(whitelistItem1)(privilegeItem, whitelistItem1);
getCompareFn(whitelistItem2)(privilegeItem, whitelistItem2);

链接到Try Flow

Flow的declare用于固定getCompareFn的精确类型签名,而该类型签名否则很难表达。注意流程不会强制您声明的正确性,因此您有责任仔细分析并确保声明的类型确实与函数的实现相匹配,尽管在此示例中,这很简单。

在不使用declare的情况下,我们可以执行以下操作:

// type alias to represent generic compare function
type CompareFn<T> = (string, T) => boolean;

function getCompareFn(
  whitelistItem: RegExp | string
): CompareFn<string> | CompareFn<RegExp> {
  return whitelistItem instanceof RegExp ? patternMatch : equal;
}

到目前为止,它可以进行检查。 (但是请注意,getCompareFn的返回值不是CompareFn<RegExp | string>)。不幸的是,getCompareFn的这种类型说明不够精确。例如,我们知道以下事实是正确的:

const f: CompareFn<string> = getCompareFn('string');

但是,流程无法断言。

再进一步,我们可以尝试解决此问题:

function getCompareFn<T: RegExp | string>(whitelistItem: T): CompareFn<T> {
  return whitelistItem instanceof RegExp ? patternMatch : equal;
}

这比上面的要好得多,更确切地说,泛型函数getCompareFn指出:

  1. getCompareFn(string) -> CompareFn<string>
  2. getCompareFn(RegExp) -> CompareFn<RegExp>

这正是代码的意图。一切都很好?不幸的是,此代码未进行类型检查。问题是T: string | RegExp,它指出:对于类型为string | RegExp的所有子类型,getCompareFn返回CompareFn<T>。转换为:

  1. getCompareFn(string) -> CompareFn<string>
  2. getCompareFn(RegExp) -> CompareFn<RegExp>
  3. getCompareFn(string | RegExp) -> CompareFn<string | RegExp);

最后一种情况显然是不正确的,因为getCompareFn永远不会返回接受字符串和RegExp的比较函数。因此,我们要表达情况1和2,但不允许情况3,而我知道要这样做的唯一工具是declare,因此该答案首先概述了解决方案。

最后,通过稍微更改代码,这是不使用declare的解决方案,还有一些惯用法(在我看来):Link

const whitelistItem1 = /test/;
const whitelistItem2 = "test";

const privilegeItem = "test";

interface TestString {
  test(s: string): boolean;  
};

// RegExp has method `test`, therefore structurally implements TestString

class EqualsString implements TestString {
   _value: string;
   constructor(value: string) {
      this._value = value; 
   }
   test(s) {
      return s === this._value; 
   }
}

function getStringTester(whitelistItem: string | RegExp): TestString {
  if (whitelistItem instanceof RegExp) {
    return whitelistItem;
  } else {
    return new EqualsString(whitelistItem);
  }
}

const t1 = getStringTester(whitelistItem1);
const t2 = getStringTester(whitelistItem2);

const b1: boolean = t1.test(privilegeItem);
const b2: boolean = t2.test(privilegeItem);