Как типизировать функцию обратного вызова (callback)?
Функции обратного вызова (callback) активно применяются в TypeScript при работе с асинхронными операциями, обработчиками событий и функциональными методами массивов. Правильная типизация callback помогает избежать ошибок при передаче аргументов и гарантирует, что разработчик использует функцию именно так, как задумано.
Базовый принцип типизации callback
Callback — это просто функция, передаваемая в другую функцию как аргумент. Чтобы её типизировать, необходимо указать типы параметров и возвращаемого значения.
Пример:
function processData(data: string, callback: (result: number) => void): void {
const processed = data.length;
callback(processed);
}
Здесь callback принимает число и ничего не возвращает (void).
Использование:
processData("hello", (len) => {
console.log(\`Длина строки: ${len}\`);
});
Использование алиасов типов и интерфейсов
Чтобы не дублировать сигнатуру callback в разных местах, удобно вынести её в отдельный тип или интерфейс.
Через type:
type Callback = (value: number, index: number) => void;
function iterateArray(arr: number\[\], cb: Callback): void {
arr.forEach(cb);
}
Через interface:
interface Callback {
(value: string): boolean;
}
function filterStrings(arr: string\[\], cb: Callback): string\[\] {
return arr.filter(cb);
}
Оба подхода упрощают читаемость и повышают переиспользуемость кода.
Опциональные параметры в callback
Иногда функция обратного вызова может принимать разное количество параметров. В таких случаях можно указать опциональные аргументы:
type Logger = (message: string, level?: string) => void;
function log(cb: Logger) {
cb("Система запущена");
cb("Ошибка соединения", "error");
}
Callback с возвращаемым значением
Важно помнить, что callback может возвращать значение. Например, метод map ожидает, что callback вернёт преобразованное значение.
function transformArray(arr: number\[\], cb: (el: number) => string): string\[\] {
return arr.map(cb);
}
const result = transformArray(\[1, 2, 3\], (num) => \`Число: ${num}\`);
В этом примере функция обратного вызова возвращает строку, и результирующий массив будет строковым.
Универсальные (generic) callback
В случаях, когда callback должен работать с разными типами данных, применяются дженерики.
function applyOperation<T, R>(value: T, cb: (input: T) => R): R {
return cb(value);
}
const numToStr = applyOperation(42, (n) => n.toString()); // string
const strToLen = applyOperation("hello", (s) => s.length); // number
Здесь callback получает значение одного типа (T) и возвращает значение другого (R). Такой подход делает функцию максимально гибкой.
Контекст (this) в callback
Иногда необходимо указать тип контекста this внутри callback. TypeScript позволяет это сделать явно.
interface User {
name: string;
}
function runWithUser(cb: (this: User) => void) {
cb.call({ name: "Иван" });
}
runWithUser(function () {
console.log(this.name); // "Иван"
});
Здесь указывается, что внутри callback this будет объектом типа User.
Таким образом, типизация callback в TypeScript может быть простой или довольно сложной, в зависимости от сценария. Она обеспечивает контроль над входными и выходными значениями функции, помогает создавать безопасный код и уменьшает количество ошибок при интеграции разных частей системы.