Сокращение набора предикатных функций - PullRequest
0 голосов
/ 11 октября 2018

У меня есть массив предикатов, входящих в функцию, которую мы назовем 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, поэтому он вводит разрывы кода с этим дополнительным ограничением.Я уверен, что упускаю что-то абсурдное, я просто не могу сказать, что.

1 Ответ

0 голосов
/ 11 октября 2018

Вам просто нужно предоставить начальное значение для функции 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

...