减少一组谓词函数

Reducing a set of predicate functions

我有一组谓词进入我们将调用的函数 searchForAcceptableNumber

searchForAcceptableNumber(arrayOfNumbers: number[], ...isNumberAcceptablePredicates: Array<(aNumber: number) => boolean>): number[] {
    const acceptableNumbers: number[] = [];

    const isNumberAcceptablePredicate: (aNumber: number) => boolean = (aNumber: number) => {
        const isLabelAcceptableReducer = (accumulator, currentValue) => accumulator && currentValue(aNumber);
        return isNumberAcceptablePredicates.reduce(isLabelAcceptableReducer);
    };

    arrayOfNumbers.forEach((aNumber: number) => {
        if (isNumberAcceptablePredicate(aNumber)) {
            acceptableNumbers.push(aNumber);
        }
    });
    return acceptableNumbers;
}

你懂的。从本质上讲,它遍历了一切,一切都很好……除了 isNumberAcceptablePredicate 不断收到类型 (aNumber: number) => (aNumber: number) => boolean 的错误这一事实。我可以摆脱类型,它会全部编译并工作得很好。奇怪的是,如果我在调试会话期间将鼠标悬停在 isNumberAcceptablePredicate 上,它说它确实是 (aNumber: number) => boolean.

类型

这不一定是代码破坏,也就是说,我最终将这个谓词传递给需要类型 (aNumber: number) => boolean 的其他函数,因此它确实引入了代码破坏并增加了限制。我确定我错过了一些荒谬的东西,我只是不知道是什么。

您只需要为 reduce 函数提供一个初始值,否则它默认使用数组中的第一个元素作为初始值,其类型为 (aNumber: number) => boolean 而不是 boolean

function searchForAcceptableNumber(
  arrayOfNumbers: number[],
  ...isNumberAcceptablePredicates: Array<(aNumber: number) => boolean>
): number[] {
  const acceptableNumbers: number[] = []

  const isNumberAcceptablePredicate: (aNumber: number) => boolean = (
    aNumber: number,
  ) => {
    const isLabelAcceptableReducer = (accumulator, currentValue) =>
      accumulator && currentValue(aNumber)
    return isNumberAcceptablePredicates.reduce(isLabelAcceptableReducer, true) // <-- initialise to true
  }

  arrayOfNumbers.forEach((aNumber: number) => {
    if (isNumberAcceptablePredicate(aNumber)) {
      acceptableNumbers.push(aNumber)
    }
  })
  return acceptableNumbers
}

您还可以使用一些内置的数组函数来大大缩短此函数,例如

function searchForAcceptableNumber(
  arrayOfNumbers: number[],
  ...isNumberAcceptablePredicates: Array<(aNumber: number) => boolean>
): number[] {
  return arrayOfNumbers.filter(number =>
    isNumberAcceptablePredicates.every(predicate => predicate(number)),
  )
}

如果您想概括实施,您可以执行以下操作

function filterList<T>(
  values: T[],
  predicates: Array<(val: T) => boolean>,
): T[] {
  return values.filter(val => predicates.every(pred => pred(val)))
}

你也可以使用箭头函数,因为函数只返回一个值,有些人更喜欢它的简洁性

const filterList = <T extends any>(
  values: T[],
  predicates: Array<(val: T) => boolean>,
): T[] => values.filter(val => predicates.every(pred => pred(val)))

注:<T extends any>的原因虽然extends any在语义上是多余的,但还是有必要的,因为单独的<T>在句法上是有歧义的,可能被视为JSX 标签