TypeScript функция как аргумент: как типизировать callback

Короткий ответ: функцию как аргумент в TypeScript типизируют через сигнатуру callback: какие параметры она принимает и что возвращает. Например, (value: number) => string означает: функция принимает число и возвращает строку

Простой пример:

function formatPrice(value: number, formatter: (value: number) => string): string {
  return formatter(value);
}

const result = formatPrice(1000, (value) => `${value} руб.`);

TypeScript понимает, что value внутри callback — число, а результат всей функции — строка

Вынести тип callback

Если callback используется несколько раз, лучше вынести тип:

type PriceFormatter = (value: number) => string;

function formatPrice(value: number, formatter: PriceFormatter): string {
  return formatter(value);
}

Так код читается легче, особенно если у функции несколько параметров

Callback без возвращаемого значения

Если функция ничего не возвращает:

type Logger = (message: string) => void;

function runTask(logger: Logger) {
  logger("Задача запущена");
}

void означает, что вызывающий код не должен рассчитывать на результат

Несколько параметров

type CompareFn<T> = (a: T, b: T) => number;

function sortItems<T>(items: T[], compare: CompareFn<T>): T[] {
  return [...items].sort(compare);
}

Использование:

const numbers = sortItems([3, 1, 2], (a, b) => a - b);

TypeScript выводит, что a и b — числа

Функция как параметр в объекте

Часто callback передают в props:

type ButtonProps = {
  label: string;
  onClick: () => void;
};

function Button(props: ButtonProps) {
  props.onClick();
}

Для callback с данными:

type User = {
  id: number;
  name: string;
};

type UserListProps = {
  users: User[];
  onSelect: (user: User) => void;
};

Так дочерний компонент может передать выбранного пользователя наверх, а TypeScript проверит форму объекта

Async callback

Если callback асинхронный:

type SaveUser = (name: string) => Promise<void>;

async function submit(name: string, saveUser: SaveUser) {
  await saveUser(name);
}

Не пишите просто Function. Такой тип не говорит, какие параметры нужны и что вернется

Generic callback

Универсальный обработчик:

function mapItems<T, R>(items: T[], mapper: (item: T) => R): R[] {
  return items.map(mapper);
}

const lengths = mapItems(["one", "three"], (word) => word.length);

Здесь T — тип элемента входного массива, R — тип результата callback. TypeScript выводит lengths как number[]

Частые ошибки

Первая ошибка — писать callback: Function. Это почти не дает проверки

Вторая ошибка — ставить any в параметрах callback

Третья ошибка — забывать возвращаемый тип, когда результат важен

Четвертая ошибка — путать функцию и результат вызова:

runTask(logger());

Если функция ожидает callback, нужно передать функцию:

runTask(logger);

Пятая ошибка — делать тип callback слишком широким. Если нужен User, не принимайте object

Когда callback лучше не передавать

Функция как аргумент полезна, когда поведение действительно должно быть настраиваемым: форматирование, фильтрация, обработка события, логирование. Но если callback просто скрывает одну строку бизнес-логики, код может стать менее понятным

Плохой признак:

function processUser(user: User, callback: (user: User) => void) {
  callback(user);
}

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

Также не смешивайте callback с побочными эффектами без названия. onSave, onSelect, formatLabel, predicate читаются лучше, чем абстрактный fn

Самопроверка

Напишите функцию filterItems<T>(items: T[], predicate: (item: T) => boolean): T[]. Вызовите ее для массива чисел и массива пользователей. Если TypeScript правильно понимает тип item в обоих случаях, callback типизирован верно

Что почитать дальше по TypeScript

Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x