В этой статье я хочу кратко объяснить, что такое функции высшего порядка (сокр. ФВП) и как их использовать. Если вы не знакомы с ФВП, тогда вы будете удивлены, ведь работаете с ними постоянно что на JavaScript, что на TypeScript.

Что такое функции высшего порядка?

Функция высшего порядка - это функция, которая либо принимает другую функцию в качестве аргумента, либо возвращает функцию. Проще говоря, можно сказать, что функция высшего порядка - это обертка для другой функции.

Вы можете спросить: Зачем функции принимать другую функцию в качестве аргумента?

Самый простой способ объяснить этот функционал в JavaScript экосистеме - это колбэки.

Колбэки повсюду, так что функции высшего порядка встречаются сплошь и рядом.

Функции высшего порядка, которые вы могли знать

Давайте посмотрим на самые популярные функции высшего порядка, с которыми вы точно сталкивались при разработке на JavaScript или TypeScript.

setTimeout()

Да, setTimeout - это функция высшего порядка. Она принимает другую функцию в качестве аргумента и вызывает ее по прошествии определенного количества времени. В данном примере, по прошествии примерно 100 миллисекунд.

setTimeout(() => {
  console.log('Hello');
}, 100);

Array.find()

Array.find() также очень популярная функция высшего порядка, которая появляется в большинстве проектов по несколько раз. Но вообще-то она чуть более сложная. Потому что наша функция внутри find() просто возвращает булево значение, но сама Array.find() возвращает либо undefined, либо искомый элемент.

const arr: Record<string, number>[] = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];

const res: Record<string, number> = arr.find((item: Record<string, number>) => {
  return item.id === 3;
});

// res = { id: 3 }

Array.filter()

Еще одна функция класса Array используется для фильтрации массивов. Эта функция также принимает другую функцию как аргумент и возвращает новый отфильтрованный массив.

const arr: string[] = ['Anna', 'Emily', 'John', 'Kevin', 'Michelle', 'Ryan', 'Yvonne'];

const res: string[] = arr.filter((name: string) => {
  return name.includes('i');
});

// res = ['Emily', 'Kevin', 'Michelle']

Создание своей функции высшего порядка

Вышеприведенные примеры показывают, как ФВП принимают другие функции в качестве аргумента, поэтому я хочу показать второй вариант использования функций высшего порядка, когда они возвращают функцию.

Для начала мы создадим новую функцию и назовем ее requiredAge, она будет принимать число как аргумент. Это число - это минимальный требуемый возраст. А возвращать она будет другую функцию, которая в свою очередь будет возвращать булево значение.

Затем мы вызовем эту функцию дважды на 7 и 8 строчках, где мы установим минимальный возраст.

В конце мы вызовем внутреннюю функцию requiredAge 4 раза, проверяя, подходит ли наш возраст.

function requiredAge(minimum: number): Function {
  return (age: number): boolean => {
    return age >= minimum;
  };
}

const canDrinkBeer: Function = requiredAge(16);
const canDriveCar: Function = requiredAge(18);

console.log(canDrinkBeer(10)); // -> вернет false
console.log(canDrinkBeer(20)); // -> вернет true

console.log(canDriveCar(17)); // -> вернет false
console.log(canDriveCar(25)); // -> вернет true

Что ж, этот концепт звучит довольно сложно, но на самом деле это не так, и мы постоянно имеем дело с функциями высшего порядка.

Спасибо за прочтение. Надеюсь, я смог освежить ваши знания, а, возможно, вы узнали для себя что-то новое, что еще лучше.

Комментарии (4)


  1. Surof1n
    14.03.2022 08:42

    Друзья, пожалуйста, подумайте прежде чем использовать реализации из этой статьи, на ревью вам не скажут спасибо.

    Если вам действительно требуется что-то подобное, то оставьте ваши типы в покое и не пишите явные аннотации типов в этих примерах:

    function requiredAge(minimum: number) {
      return (age: number): boolean => {
        return age >= minimum;
      };
    }
    
    const canDrinkBeer = requiredAge(16);
    const canDriveCar = requiredAge(18);
    
    console.log(canDrinkBeer(10)); 
    // -> return: false, IntelliSense: boolean
    console.log(canDriveCar(25)); 
    // -> return: true, Intellisense: boolean
    const arr = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
    
    const res = arr.find((item) => {
      return item.id === 3;
    });
    // IntelliSense: { id: number } | undefined


    Я бы хотел разобрать данный пример дважды

    // Я бы хотел приблизить данный пример к действительности и чаще всего
    // у вас не будет возможность иметь в коде - LiteralTypes
    
    const arr = ['Anna', 'Emily', 'John', 'Kevin', 'Michelle', 'Ryan', 'Yvonne'];
    // IntelliSense: string[]
    
    const res = arr.filter((name) => {
      return name.includes('i');
    });
    // IntelliSense: string[]
    // res = ['Emily', 'Kevin', 'Michelle']

    Если вам действительно потребуется фильтрация массива в котором находятся данные литералы, то взгляните на данную реализацию:

    type Filter<T extends readonly unknown[]> = T extends []
      ? []
      : T extends readonly [infer H, ...infer R]
      ? H extends undefined
        ? Filter<R>
        : [H, ...Filter<R>]
      : T;
    
    type FilterableLiteralArray<
      A extends readonly string[],
      Index extends string
    > = Filter<{
      [key in keyof A]: A[key] extends `${infer First}${Index}${infer Second}`
        ? A[key]
        : undefined;
    }>;
    
    const arr = [
      "Anna",
      "Emily",
      "John",
      "Kevin",
      "Michelle",
      "Ryan",
      "Yvonne",
    ] as const;
    
    function useFilter<Arr extends readonly string[], T extends string>(
      array: Arr,
      literal: T
    ) {
      return array.filter((name) => {
        return name.includes(literal);
      }) as FilterableLiteralArray<Arr, T>;
    }
    
    const filterableArrayFirst = useFilter(arr, "i");
    // const filterableArrayFirst: ["Emily", "Kevin", "Michelle"]
    const filterableArraySecond = useFilter(arr, "E");
    // const filterableArraySecond: ["Emily"]
    


    1. muturgan
      16.03.2022 08:14

      Я предпочитаю везде явно задавать возвращаемые типы.

      Просто для функции requireAge в качестве возвращаемого типа следует указать не Function а более отражающее суть (age: number) => boolean


  1. alexmcgil
    15.03.2022 08:45
    +1

    Да, это перевод. Но какую пользу привносит данная статья, особенно, в отношении ts?


    1. muturgan
      16.03.2022 08:16

      Всё так