TypeScript Callbacks
Pass functions as arguments with type safety
🔄 What are Callbacks?
Callbacks are functions passed as arguments to other functions and executed later. TypeScript allows you to define callback types, ensuring the passed functions have correct parameters and return types for safer asynchronous operations.
// Function that accepts a callback
function processData(data: number, callback: (result: number) => void): void {
const result = data * 2;
callback(result);
}
processData(5, (result) => {
console.log(`Result: ${result}`);
});
Output:
Result: 10
Callback Concepts
Function Parameters
Pass functions as arguments
function execute(
callback: () => void
) {
callback();
}
Type Safety
Define callback signatures
type Callback = (
data: string
) => void;
Async Operations
Handle delayed execution
setTimeout(() => {
console.log("Done!");
}, 1000);
Event Handling
Respond to events
button.addEventListener(
"click",
() => { }
);
🔹 Basic Callback Example
Create a function that accepts a callback:
// Function with callback parameter
function greetUser(name: string, callback: (message: string) => void): void {
const greeting = `Hello, ${name}!`;
callback(greeting);
}
// Call with callback
greetUser("Alice", (message) => {
console.log(message);
});
// Call with different callback
greetUser("Bob", (message) => {
console.log(`Message received: ${message}`);
});
Output:
Hello, Alice!
Message received: Hello, Bob!
🔹 Callbacks with Return Values
Callbacks can return values:
// Callback that returns a value
function calculate(
a: number,
b: number,
operation: (x: number, y: number) => number
): number {
return operation(a, b);
}
// Different operations
const sum = calculate(10, 5, (x, y) => x + y);
const product = calculate(10, 5, (x, y) => x * y);
const difference = calculate(10, 5, (x, y) => x - y);
console.log("Sum:", sum);
console.log("Product:", product);
console.log("Difference:", difference);
Output:
Sum: 15
Product: 50
Difference: 5
🔹 Callback Type Aliases
Define reusable callback types:
// Define callback types
type SuccessCallback = (data: string) => void;
type ErrorCallback = (error: string) => void;
function fetchData(
url: string,
onSuccess: SuccessCallback,
onError: ErrorCallback
): void {
// Simulate API call
const success = url.includes("valid");
if (success) {
onSuccess("Data loaded successfully!");
} else {
onError("Failed to load data");
}
}
// Use the function
fetchData(
"valid-url",
(data) => console.log("Success:", data),
(error) => console.log("Error:", error)
);
fetchData(
"invalid-url",
(data) => console.log("Success:", data),
(error) => console.log("Error:", error)
);
Output:
Success: Data loaded successfully!
Error: Failed to load data
🔹 Array Methods with Callbacks
Common array methods use callbacks:
const numbers: number[] = [1, 2, 3, 4, 5];
// forEach callback
numbers.forEach((num: number, index: number) => {
console.log(`Index ${index}: ${num}`);
});
console.log("---");
// map callback
const doubled = numbers.map((num: number) => num * 2);
console.log("Doubled:", doubled);
// filter callback
const evens = numbers.filter((num: number) => num % 2 === 0);
console.log("Evens:", evens);
Output:
Index 0: 1
Index 1: 2
Index 2: 3
Index 3: 4
Index 4: 5
---
Doubled: [2, 4, 6, 8, 10]
Evens: [2, 4]
🔹 Asynchronous Callbacks
Use callbacks for async operations:
// Simulate async operation
function loadUser(
userId: number,
callback: (user: { id: number; name: string }) => void
): void {
console.log("Loading user...");
// Simulate delay
setTimeout(() => {
const user = { id: userId, name: "Alice" };
callback(user);
}, 1000);
}
loadUser(1, (user) => {
console.log(`User loaded: ${user.name} (ID: ${user.id})`);
});
Output:
Loading user...
(after 1 second)
User loaded: Alice (ID: 1)
🔹 Practical Example: Event System
Build a simple event system with callbacks:
type EventCallback = (data: any) => void;
class EventEmitter {
private events: { [key: string]: EventCallback[] } = {};
on(event: string, callback: EventCallback): void {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event: string, data: any): void {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data));
}
}
}
const emitter = new EventEmitter();
emitter.on("userLogin", (username: string) => {
console.log(`User logged in: ${username}`);
});
emitter.on("userLogin", (username: string) => {
console.log(`Welcome back, ${username}!`);
});
emitter.emit("userLogin", "Alice");
Output:
User logged in: Alice
Welcome back, Alice!
💡 Callback Best Practices:
- Always define callback parameter and return types
- Use type aliases for complex callback signatures
- Consider using Promises or async/await for complex async operations
- Avoid "callback hell" by keeping nesting shallow
- Handle errors appropriately in callbacks