Как правильно объявить тип для callback-функции в TypeScript?
Основной способ: сигнатура вызова (call signature)
Лучший способ объявить тип callback-функции — использовать сигнатуру вызова (call signature) в TypeScript. Это позволяет явно указать, какие параметры принимает функция и что она возвращает.
В вашем случае, когда callback принимает один аргумент типа string и ничего не возвращает (void), правильный тип будет:
type CallbackType = (result: string) => void;
function processData(data: string, callback: CallbackType): void {
const result = data.toUpperCase();
callback(result);
}
// Использование:
processData("hello", (res) => {
console.log(res); // HELLO
});
Тип void означает, что возвращаемое значение callback-а игнорируется. Вы можете передать функцию, которая возвращает число или что-то ещё — ошибки не будет, но результат не будет использован.
Объявление типа на месте (inline)
Можно не создавать отдельный тип, а написать сигнатуру прямо в параметре:
function processData(data: string, callback: (result: string) => void): void {
const result = data.toUpperCase();
callback(result);
}
Callback с несколькими параметрами
Если callback принимает несколько аргументов, просто перечислите их в сигнатуре:
type UserCallback = (id: number, name: string, isActive: boolean) => void;
function fetchUser(id: number, callback: UserCallback): void {
// Имитация запроса
const user = { id, name: "Alice", isActive: true };
callback(user.id, user.name, user.isActive);
}
fetchUser(1, (id, name, isActive) => {
console.log(`${name} (ID: ${id}) активен: ${isActive}`);
});
Опциональный callback
Чтобы сделать callback необязательным, используйте оператор ? или объединение с undefined:
function processData(data: string, callback?: (result: string) => void): void {
const result = data.toUpperCase();
if (callback) {
callback(result);
}
}
// Можно вызвать без callback:
processData("hello");
// Или с callback:
processData("hello", (res) => console.log(res));
Callback, который может вернуть значение
Если callback должен вернуть какое-то значение (например, трансформировать данные), укажите тип возврата:
type Transformer = (input: string) => number;
function transformAndLog(data: string, transformer: Transformer): void {
const result = transformer(data);
console.log(`Результат трансформации: ${result}`);
}
transformAndLog("42", (str) => parseInt(str)); // Результат: 42
Использование обобщений (generics) для гибкости
Если callback должен работать с разными типами данных, можно сделать функцию обобщённой:
function processData<T>(data: T, callback: (value: T) => void): void {
callback(data);
}
processData("строка", (val) => console.log(val.length)); // 6
processData(123, (val) => console.log(val.toFixed(2))); // 123.00
Что НЕ стоит использовать
Function— слишком общий тип, не даёт контроля над параметрамиany— отключает проверку типов полностьюobject— не подходит для функций
Используйте сигнатуру вызова — это самый безопасный и читаемый способ объявить callback в TypeScript.
<<Альтернативный подход: интерфейсы с сигнатурой вызова
Вместо type можно использовать интерфейс с сигнатурой вызова. Это особенно удобно, если callback — это часть более сложного объекта или вы хотите добавить документацию:
interface StringCallback {
(result: string): void;
description?: string; // можно добавить свойства
}
function processData(data: string, callback: StringCallback): void {
const result = data.toUpperCase();
callback(result);
}
const myCallback: StringCallback = (res) => {
console.log(res);
};
myCallback.description = "Просто логирует результат";
processData("hello", myCallback);
Callback с ошибками (Node.js стиль)
Если вы пишете код в стиле Node.js (первый аргумент — ошибка), используйте такой тип:
type NodeCallback<T> = (error: Error | null, result?: T) => void;
function readFile(path: string, callback: NodeCallback<string>): void {
if (!path) {
callback(new Error("Путь не указан"));
return;
}
callback(null, "содержимое файла");
}
readFile("/test.txt", (err, data) => {
if (err) {
console.error(err.message);
return;
}
console.log(data);
});
Этот подход широко используется в старых библиотеках и асинхронных операциях.