Prerequisites
- Basic JavaScript function knowledge ๐
- Understanding of TypeScript basics โก
- Familiarity with ES6 features ๐ป
What you'll learn
- Write concise arrow functions with types ๐ฏ
- Understand lexical this binding ๐๏ธ
- Apply arrow functions in real scenarios ๐
- Choose between arrow and regular functions โจ
๐ฏ Introduction
Welcome to the sleek world of arrow functions in TypeScript! ๐ In this guide, weโll explore how these modern function expressions make your code cleaner, more concise, and more predictable.
Think of arrow functions as the sports car ๐๏ธ of the function world - streamlined, efficient, and designed for the modern era. Theyโre not just about shorter syntax; they fundamentally change how this
works and make functional programming patterns a breeze!
By the end of this tutorial, youโll be writing arrow functions like a TypeScript ninja, knowing exactly when to use them and when to stick with traditional functions! Letโs dive in! ๐โโ๏ธ
๐ Understanding Arrow Functions
๐ค What Are Arrow Functions?
Arrow functions are like shortcuts on your keyboard โจ๏ธ - they help you write the same thing with less effort! Introduced in ES6, they provide a concise way to write function expressions.
The syntax looks like this: () => {}
- hence the name โarrowโ functions! โก๏ธ
Key characteristics:
- โจ Concise syntax for function expressions
- ๐ฏ Lexical
this
binding (inherits from surrounding scope) - ๐ก๏ธ Implicit returns for single expressions
- ๐ Perfect for callbacks and functional programming
๐ก Why Use Arrow Functions?
Hereโs why arrow functions are game-changers:
- Cleaner Code ๐งฝ: Less boilerplate, more readable
- Predictable
this
๐ฏ: No morethat = this
hacks! - Functional Programming ๐: Perfect for map, filter, reduce
- Type Inference ๐ค: TypeScript often infers types automatically
- Modern Standards ๐: Industry standard in modern JavaScript/TypeScript
Real-world example: In React components โ๏ธ, arrow functions eliminate the need to bind methods in constructors!
๐ง Basic Syntax and Usage
๐ Arrow Function Syntax
Letโs compare traditional functions with arrow functions:
// ๐ญ Traditional function
function add(a: number, b: number): number {
return a + b;
}
// โก๏ธ Arrow function - same thing!
const addArrow = (a: number, b: number): number => {
return a + b;
};
// ๐ฏ Even shorter - implicit return!
const addShort = (a: number, b: number): number => a + b;
// โจ Type inference - TypeScript knows the return type
const addInferred = (a: number, b: number) => a + b; // Return type inferred as number
// ๐จ Single parameter - parentheses optional
const double = (n: number) => n * 2;
const doubleNoParens = n => n * 2; // Works in JS, but TS needs type!
const doubleTyped = (n: number) => n * 2; // Better for TypeScript
// ๐ No parameters
const getRandom = (): number => Math.random();
const sayHello = () => console.log("Hello! ๐");
๐ฏ Common Patterns
Here are arrow function patterns youโll use daily:
// ๐๏ธ Pattern 1: Array methods
const numbers = [1, 2, 3, 4, 5];
// Map
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// Filter
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
// Reduce
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(`Sum: ${sum} ๐งฎ`);
// ๐จ Pattern 2: Object methods with arrow functions
interface Calculator {
value: number;
add: (n: number) => void;
multiply: (n: number) => void;
getValue: () => number;
}
const calculator: Calculator = {
value: 0,
add: function(n: number) { // Regular function to access this
this.value += n;
},
multiply: function(n: number) {
this.value *= n;
},
getValue: function() {
return this.value;
}
};
// ๐ Pattern 3: Callbacks and event handlers
const button = document.querySelector('button');
button?.addEventListener('click', (event) => {
console.log('๐ Button clicked!', event);
});
// ๐ Pattern 4: Promises and async
const fetchData = async (): Promise<string> => {
const response = await fetch('/api/data');
return response.text();
};
// Chain with arrow functions
fetchData()
.then(data => console.log('๐ฆ Data:', data))
.catch(error => console.error('โ Error:', error));
๐งฉ Type Annotations in Arrow Functions
// ๐ฏ Full type annotation
const greet: (name: string) => string = (name) => {
return `Hello, ${name}! ๐`;
};
// ๐จ Return type annotation
const calculate = (x: number, y: number): { sum: number; product: number } => ({
sum: x + y,
product: x * y
});
// ๐ก๏ธ Generic arrow functions
const identity = <T>(value: T): T => value;
const first = <T>(array: T[]): T | undefined => array[0];
// ๐ Complex types
type MathOperation = (a: number, b: number) => number;
const operations: Record<string, MathOperation> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => b !== 0 ? a / b : 0
};
console.log(operations.add(5, 3)); // 8
console.log(operations.multiply(4, 7)); // 28
๐ก Practical Examples
๐ Example 1: Functional Shopping Cart
Letโs build a shopping cart using arrow functions:
// ๐๏ธ Product type
interface Product {
id: string;
name: string;
price: number;
category: string;
emoji: string;
}
// ๐ Functional shopping cart using arrow functions
class ShoppingCart {
private items: Product[] = [];
// โก๏ธ Arrow functions for all methods
addItem = (product: Product): void => {
this.items.push(product);
console.log(`${product.emoji} ${product.name} added to cart!`);
};
removeItem = (productId: string): void => {
this.items = this.items.filter(item => item.id !== productId);
console.log(`๐๏ธ Item removed from cart`);
};
// ๐ฐ Calculate with arrow functions
getTotal = (): number =>
this.items.reduce((sum, item) => sum + item.price, 0);
getTax = (rate: number = 0.08): number =>
this.getTotal() * rate;
getFinalPrice = (): number =>
this.getTotal() + this.getTax();
// ๐ฏ Filter and map operations
getItemsByCategory = (category: string): Product[] =>
this.items.filter(item => item.category === category);
getItemNames = (): string[] =>
this.items.map(item => `${item.emoji} ${item.name}`);
// ๐จ Summary with template literals
getSummary = (): string => {
const total = this.getTotal();
const tax = this.getTax();
const final = this.getFinalPrice();
return `
๐ Cart Summary:
Items: ${this.items.length}
Subtotal: $${total.toFixed(2)}
Tax: $${tax.toFixed(2)}
Total: $${final.toFixed(2)} ๐ณ
`.trim();
};
// ๐ Async operations
checkout = async (): Promise<{ success: boolean; orderId: string }> => {
console.log('๐ฆ Processing order...');
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
const orderId = `ORD-${Date.now()}`;
console.log(`โ
Order ${orderId} placed successfully!`);
return { success: true, orderId };
};
}
// ๐ฎ Usage
const cart = new ShoppingCart();
cart.addItem({
id: "1",
name: "TypeScript Handbook",
price: 39.99,
category: "books",
emoji: "๐"
});
cart.addItem({
id: "2",
name: "Mechanical Keyboard",
price: 149.99,
category: "electronics",
emoji: "โจ๏ธ"
});
console.log(cart.getSummary());
console.log("๐ท๏ธ Electronics:", cart.getItemsByCategory("electronics"));
// Async checkout
cart.checkout().then(result => {
console.log("๐ Order complete:", result);
});
๐จ Example 2: Event System with Arrow Functions
Letโs create an event system showcasing this
binding:
// ๐ Event emitter using arrow functions
type EventCallback<T = any> = (data: T) => void;
class EventEmitter {
private events = new Map<string, EventCallback[]>();
// โก๏ธ Arrow function preserves 'this'
on = <T>(event: string, callback: EventCallback<T>): void => {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(callback);
console.log(`๐ Subscribed to "${event}"`);
};
off = <T>(event: string, callback: EventCallback<T>): void => {
const callbacks = this.events.get(event);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
console.log(`๐ Unsubscribed from "${event}"`);
}
}
};
emit = <T>(event: string, data: T): void => {
const callbacks = this.events.get(event) || [];
console.log(`๐ก Emitting "${event}" to ${callbacks.length} listeners`);
// Arrow function in forEach preserves 'this'
callbacks.forEach(callback => {
callback(data);
});
};
// ๐ Async emit
emitAsync = async <T>(event: string, data: T): Promise<void> => {
const callbacks = this.events.get(event) || [];
await Promise.all(
callbacks.map(callback =>
Promise.resolve(callback(data))
)
);
};
}
// ๐ฎ Game using event system
class Game {
private events = new EventEmitter();
private score = 0;
private level = 1;
constructor() {
// Arrow functions automatically bind 'this'
this.setupEventHandlers();
}
private setupEventHandlers = () => {
// No need for .bind(this)!
this.events.on('score', this.handleScore);
this.events.on('powerup', this.handlePowerup);
this.events.on('gameover', this.handleGameOver);
};
// โก๏ธ All handlers use arrow functions
private handleScore = (points: number) => {
this.score += points;
console.log(`๐ฏ Score: ${this.score}`);
if (this.score >= this.level * 100) {
this.levelUp();
}
};
private handlePowerup = (type: string) => {
console.log(`โก Power-up collected: ${type}`);
const bonuses: Record<string, () => void> = {
'double': () => this.score *= 2,
'shield': () => console.log('๐ก๏ธ Shield activated!'),
'speed': () => console.log('๐ Speed boost!')
};
bonuses[type]?.();
};
private handleGameOver = () => {
console.log(`๐ฎ Game Over! Final score: ${this.score}`);
};
private levelUp = () => {
this.level++;
console.log(`๐ Level ${this.level}!`);
};
// Public methods
play = () => {
console.log('๐ฎ Game started!');
// Simulate game events
setTimeout(() => this.events.emit('score', 10), 1000);
setTimeout(() => this.events.emit('powerup', 'double'), 2000);
setTimeout(() => this.events.emit('score', 50), 3000);
setTimeout(() => this.events.emit('gameover', null), 5000);
};
}
// ๐ Start the game
const game = new Game();
game.play();
๐ฎ Example 3: Functional Data Pipeline
// ๐ Data transformation pipeline using arrow functions
interface User {
id: number;
name: string;
age: number;
email: string;
isActive: boolean;
score: number;
}
const users: User[] = [
{ id: 1, name: "Alice", age: 28, email: "[email protected]", isActive: true, score: 850 },
{ id: 2, name: "Bob", age: 32, email: "[email protected]", isActive: false, score: 920 },
{ id: 3, name: "Charlie", age: 25, email: "[email protected]", isActive: true, score: 780 },
{ id: 4, name: "Diana", age: 29, email: "[email protected]", isActive: true, score: 950 }
];
// ๐ฏ Composable data operations
const pipeline = {
// Filter operations
filterActive: (users: User[]) =>
users.filter(user => user.isActive),
filterByAge: (minAge: number) => (users: User[]) =>
users.filter(user => user.age >= minAge),
filterHighScorers: (minScore: number) => (users: User[]) =>
users.filter(user => user.score >= minScore),
// Transform operations
mapToNames: (users: User[]) =>
users.map(user => user.name),
mapToEmails: (users: User[]) =>
users.map(user => user.email),
mapToSummary: (users: User[]) =>
users.map(user => ({
name: user.name,
status: user.isActive ? '๐ต Active' : '๐ด Inactive',
level: user.score > 900 ? '๐ Expert' : user.score > 800 ? '๐ฅ Advanced' : '๐ฅ Intermediate'
})),
// Aggregate operations
getAverageScore: (users: User[]) =>
users.reduce((sum, user) => sum + user.score, 0) / users.length,
getAverageAge: (users: User[]) =>
users.reduce((sum, user) => sum + user.age, 0) / users.length,
groupByStatus: (users: User[]) =>
users.reduce((groups, user) => {
const key = user.isActive ? 'active' : 'inactive';
return {
...groups,
[key]: [...(groups[key] || []), user]
};
}, {} as Record<string, User[]>)
};
// ๐ Chain operations
const activeHighScorers = pipeline.filterHighScorers(800)(
pipeline.filterActive(users)
);
console.log('๐ High scorers:', pipeline.mapToNames(activeHighScorers));
console.log('๐ Average score:', pipeline.getAverageScore(users));
console.log('๐ฅ User summaries:', pipeline.mapToSummary(users));
// ๐จ Compose functions
const compose = <T>(...fns: Array<(arg: T) => T>) => (value: T): T =>
fns.reduceRight((acc, fn) => fn(acc), value);
const getEliteUsers = compose(
pipeline.mapToNames,
pipeline.filterHighScorers(900),
pipeline.filterActive
);
console.log('๐ Elite users:', getEliteUsers(users));
๐ Advanced Concepts
๐งโโ๏ธ Lexical this
Binding
The most powerful feature of arrow functions is lexical this
binding:
// ๐ฏ Traditional function - 'this' changes based on caller
class Timer {
seconds = 0;
// โ Problem with regular functions
startBad() {
setInterval(function() {
this.seconds++; // 'this' is undefined or window!
console.log(this.seconds); // Error!
}, 1000);
}
// ๐จ Old solution - store 'this'
startOldWay() {
const that = this;
setInterval(function() {
that.seconds++;
console.log(that.seconds);
}, 1000);
}
// โ
Arrow function solution - 'this' is preserved!
start = () => {
setInterval(() => {
this.seconds++; // 'this' refers to Timer instance
console.log(`โฑ๏ธ ${this.seconds} seconds`);
}, 1000);
};
}
// ๐ React-style component example
class Component {
state = { count: 0 };
// โก๏ธ Arrow function auto-binds 'this'
handleClick = () => {
this.setState({ count: this.state.count + 1 });
console.log(`๐ข Count: ${this.state.count}`);
};
// Simulate setState
setState = (newState: any) => {
this.state = { ...this.state, ...newState };
};
render = () => {
// In real React, this would return JSX
return {
onClick: this.handleClick // No .bind(this) needed!
};
};
}
๐๏ธ Arrow Functions in Classes
Modern TypeScript patterns with arrow functions:
// ๐จ Modern class with arrow function properties
class DataService {
private baseUrl = 'https://api.example.com';
private cache = new Map<string, any>();
// โก๏ธ All methods as arrow functions
get = async <T>(endpoint: string): Promise<T> => {
const cacheKey = `${this.baseUrl}${endpoint}`;
if (this.cache.has(cacheKey)) {
console.log('๐ฆ Cache hit!');
return this.cache.get(cacheKey);
}
console.log('๐ Fetching...');
const response = await fetch(cacheKey);
const data = await response.json();
this.cache.set(cacheKey, data);
return data;
};
post = async <T, R>(endpoint: string, data: T): Promise<R> => {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
};
// ๐ฏ Higher-order functions
withRetry = <T extends any[], R>(
fn: (...args: T) => Promise<R>,
retries = 3
) => async (...args: T): Promise<R> => {
for (let i = 0; i < retries; i++) {
try {
return await fn(...args);
} catch (error) {
console.log(`โ ๏ธ Retry ${i + 1}/${retries}`);
if (i === retries - 1) throw error;
}
}
throw new Error('Unreachable');
};
// Usage
getWithRetry = this.withRetry(this.get);
postWithRetry = this.withRetry(this.post);
}
// ๐ฎ Functional programming patterns
const createCounter = (initial = 0) => {
let count = initial;
return {
increment: () => ++count,
decrement: () => --count,
getValue: () => count,
reset: () => count = initial
};
};
const counter = createCounter(10);
console.log(counter.increment()); // 11
console.log(counter.increment()); // 12
console.log(counter.getValue()); // 12
๐จ Type-Safe Function Composition
// ๐งฌ Advanced function composition with arrow functions
type Fn<T, R> = (value: T) => R;
// Compose two functions
const compose2 = <A, B, C>(
f: Fn<B, C>,
g: Fn<A, B>
): Fn<A, C> => (x: A) => f(g(x));
// Pipe functions (left to right)
const pipe = <T>(...fns: Array<(arg: any) => any>) =>
(value: T) => fns.reduce((acc, fn) => fn(acc), value);
// ๐ฏ Example transformations
const addTax = (price: number) => price * 1.2;
const formatPrice = (price: number) => `$${price.toFixed(2)}`;
const addEmoji = (price: string) => `๐ฐ ${price}`;
// Compose functions
const processPrice = compose2(
compose2(addEmoji, formatPrice),
addTax
);
console.log(processPrice(100)); // "๐ฐ $120.00"
// ๐ Curry functions with arrow syntax
const curry = <T extends any[], R>(
fn: (...args: T) => R
) => {
const curried = (...args: any[]): any => {
if (args.length >= fn.length) {
return fn(...args as T);
}
return (...nextArgs: any[]) => curried(...args, ...nextArgs);
};
return curried;
};
// Example curry usage
const multiply = (a: number, b: number, c: number) => a * b * c;
const curriedMultiply = curry(multiply);
console.log(curriedMultiply(2)(3)(4)); // 24
console.log(curriedMultiply(2, 3)(4)); // 24
console.log(curriedMultiply(2)(3, 4)); // 24
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Arrow Functions and this
in Object Literals
// โ Wrong - arrow functions don't have their own 'this'
const person = {
name: "Alice",
greet: () => {
console.log(`Hello, I'm ${this.name}`); // 'this' is not the object!
}
};
person.greet(); // "Hello, I'm undefined" ๐ญ
// โ
Correct - use regular function for object methods
const personGood = {
name: "Alice",
greet: function() {
console.log(`Hello, I'm ${this.name}! ๐`);
},
// Or method shorthand
greetShort() {
console.log(`Hi, I'm ${this.name}! ๐`);
}
};
๐คฏ Pitfall 2: Arrow Functions Canโt Be Constructors
// โ Wrong - arrow functions can't be used with 'new'
const Person = (name: string) => {
this.name = name; // Error!
};
// const person = new Person("Bob"); // TypeError!
// โ
Correct - use class or regular function
class PersonClass {
constructor(public name: string) {}
}
// Or regular function constructor
function PersonFunction(this: any, name: string) {
this.name = name;
}
const person1 = new PersonClass("Bob");
const person2 = new PersonFunction("Charlie");
๐ต Pitfall 3: Implicit Returns with Objects
// โ Wrong - thinks it's a code block!
const makeUser = (name: string) => { name: name }; // undefined!
// JavaScript thinks the {} is a code block, not an object
// โ
Correct - wrap in parentheses
const makeUserGood = (name: string) => ({ name: name });
// Or even shorter
const makeUserBest = (name: string) => ({ name });
console.log(makeUserGood("Alice")); // { name: "Alice" } โ
// ๐จ More complex example
const createResponse = (status: number, data: any) => ({
status,
data,
timestamp: new Date(),
emoji: status === 200 ? "โ
" : "โ"
});
๐ค Pitfall 4: Arguments Object
// โ Arrow functions don't have 'arguments'
const sum = () => {
// return Array.from(arguments).reduce((a, b) => a + b); // Error!
};
// โ
Use rest parameters instead
const sumGood = (...numbers: number[]): number => {
return numbers.reduce((a, b) => a + b, 0);
};
console.log(sumGood(1, 2, 3, 4)); // 10 โ
// ๐ฏ Even better with type safety
const sumTyped = <T extends number[]>(...numbers: T): number => {
return numbers.reduce((sum, n) => sum + n, 0);
};
๐ฌ Pitfall 5: Method Extraction
// โ ๏ธ Careful when extracting methods
class Counter {
count = 0;
// Regular method - 'this' depends on caller
incrementBad() {
this.count++;
}
// Arrow function - 'this' always bound
incrementGood = () => {
this.count++;
};
}
const counter = new Counter();
// โ Extracting regular method loses 'this'
const badInc = counter.incrementBad;
// badInc(); // Error! 'this' is undefined
// โ
Arrow function works when extracted
const goodInc = counter.incrementGood;
goodInc(); // Works! 'this' is still the counter
console.log(counter.count); // 1 โ
// ๐ฏ Common in event handlers
button.addEventListener('click', counter.incrementGood); // Works!
// button.addEventListener('click', counter.incrementBad); // Breaks!
๐ ๏ธ Best Practices
1. ๐ฏ Use Arrow Functions for Callbacks
// โ
Perfect for array methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
// โ
Great for promises
fetchData()
.then(data => processData(data))
.then(result => console.log(result))
.catch(error => console.error(error));
2. ๐ Keep Arrow Functions Concise
// โ
Good - concise and clear
const isAdult = (age: number) => age >= 18;
const double = (n: number) => n * 2;
const greet = (name: string) => `Hello, ${name}!`;
// โ Avoid - too complex for one line
const complexCalc = (a: number, b: number) => { const x = a * 2; const y = b * 3; return x + y + Math.sqrt(x * y); };
// โ
Better - use block body for complex logic
const complexCalcBetter = (a: number, b: number) => {
const x = a * 2;
const y = b * 3;
return x + y + Math.sqrt(x * y);
};
3. ๐ก๏ธ Use Regular Functions for Object Methods
// โ
Good - regular functions for methods that use 'this'
const calculator = {
value: 0,
add(n: number) {
this.value += n;
return this;
},
getValue() {
return this.value;
}
};
// โ
Arrow functions for callbacks within methods
const dataProcessor = {
items: [1, 2, 3],
process() {
return this.items.map(item => item * 2); // Arrow function here is fine
}
};
4. ๐จ Type Arrow Functions Properly
// โ
Good - clear type annotations
type MathFn = (a: number, b: number) => number;
const operations: Record<string, MathFn> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b
};
// โ
Generic arrow functions
const identity = <T>(value: T): T => value;
const getProperty = <T, K extends keyof T>(obj: T, key: K) => obj[key];
5. โจ Choose the Right Function Type
// โ
Use arrow functions for:
// - Callbacks and event handlers
// - Array methods (map, filter, reduce)
// - Promise chains
// - When you need lexical 'this'
class Component {
name = "MyComponent";
// Arrow function for event handler
handleClick = () => {
console.log(`Clicked ${this.name}`);
};
}
// โ
Use regular functions for:
// - Object methods
// - Constructors
// - Generator functions
// - When you need dynamic 'this'
const api = {
baseUrl: "https://api.example.com",
// Regular function to access this.baseUrl
async fetchData(endpoint: string) {
const response = await fetch(`${this.baseUrl}${endpoint}`);
return response.json();
}
};
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Task Queue System
Create a task queue system using arrow functions that showcases their strengths:
๐ Requirements:
- โ Async task queue with arrow functions
- ๐ท๏ธ Different task types and priorities
- ๐ค Progress tracking and callbacks
- ๐ Retry logic for failed tasks
- ๐จ Event-driven architecture
๐ Bonus Points:
- Add task cancellation
- Implement concurrent execution limits
- Create a task scheduler
๐ก Solution
๐ Click to see solution
// ๐ฏ Task Queue System with Arrow Functions
type TaskStatus = 'pending' | 'running' | 'completed' | 'failed';
type TaskPriority = 'low' | 'normal' | 'high';
interface Task<T = any> {
id: string;
name: string;
priority: TaskPriority;
status: TaskStatus;
execute: () => Promise<T>;
onComplete?: (result: T) => void;
onError?: (error: Error) => void;
retries: number;
maxRetries: number;
}
class TaskQueue {
private queue: Task[] = [];
private running = false;
private concurrency = 2;
private activeCount = 0;
// โก๏ธ Event handlers using arrow functions
private listeners = {
taskComplete: [] as Array<(task: Task) => void>,
taskFailed: [] as Array<(task: Task, error: Error) => void>,
queueEmpty: [] as Array<() => void>
};
// ๐ฏ Add task to queue
addTask = <T>(
name: string,
execute: () => Promise<T>,
options: {
priority?: TaskPriority;
onComplete?: (result: T) => void;
onError?: (error: Error) => void;
maxRetries?: number;
} = {}
): string => {
const task: Task<T> = {
id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
name,
execute,
priority: options.priority || 'normal',
status: 'pending',
onComplete: options.onComplete,
onError: options.onError,
retries: 0,
maxRetries: options.maxRetries || 3
};
this.queue.push(task);
this.sortQueue();
console.log(`๐ฅ Added task: ${name} (${task.priority} priority)`);
if (!this.running) {
this.start();
}
return task.id;
};
// ๐ Start processing queue
start = async (): Promise<void> => {
this.running = true;
console.log('๐ Queue started');
while (this.queue.length > 0 || this.activeCount > 0) {
while (this.activeCount < this.concurrency && this.queue.length > 0) {
const task = this.queue.shift()!;
this.processTask(task);
}
await new Promise(resolve => setTimeout(resolve, 100));
}
this.running = false;
console.log('๐ Queue completed');
this.emit('queueEmpty');
};
// ๐ฏ Process individual task
private processTask = async (task: Task): Promise<void> => {
this.activeCount++;
task.status = 'running';
console.log(`๐ Running: ${task.name}`);
try {
const result = await task.execute();
task.status = 'completed';
console.log(`โ
Completed: ${task.name}`);
task.onComplete?.(result);
this.emit('taskComplete', task);
} catch (error) {
task.retries++;
console.error(`โ Failed: ${task.name} (attempt ${task.retries}/${task.maxRetries})`);
if (task.retries < task.maxRetries) {
task.status = 'pending';
this.queue.push(task);
this.sortQueue();
} else {
task.status = 'failed';
task.onError?.(error as Error);
this.emit('taskFailed', task, error as Error);
}
} finally {
this.activeCount--;
}
};
// ๐ท๏ธ Sort queue by priority
private sortQueue = (): void => {
const priorityOrder = { high: 0, normal: 1, low: 2 };
this.queue.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
};
// ๐ก Event system
on = (event: keyof typeof this.listeners, callback: any): void => {
this.listeners[event].push(callback);
};
private emit = (event: keyof typeof this.listeners, ...args: any[]): void => {
this.listeners[event].forEach(callback => callback(...args));
};
// ๐ Get queue statistics
getStats = () => ({
pending: this.queue.filter(t => t.status === 'pending').length,
running: this.activeCount,
completed: this.queue.filter(t => t.status === 'completed').length,
failed: this.queue.filter(t => t.status === 'failed').length
});
// ๐ Cancel task
cancelTask = (taskId: string): boolean => {
const index = this.queue.findIndex(t => t.id === taskId && t.status === 'pending');
if (index > -1) {
this.queue.splice(index, 1);
console.log(`๐ซ Cancelled task: ${taskId}`);
return true;
}
return false;
};
}
// ๐ฎ Example usage
const queue = new TaskQueue();
// Add event listeners
queue.on('taskComplete', (task) => {
console.log(`๐ Task completed: ${task.name}`);
});
queue.on('taskFailed', (task, error) => {
console.log(`๐จ Task failed permanently: ${task.name}`);
});
queue.on('queueEmpty', () => {
console.log('๐ All tasks processed!');
console.log('๐ Final stats:', queue.getStats());
});
// Add various tasks
queue.addTask(
'Fetch user data',
async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
return { id: 1, name: 'John Doe' };
},
{
priority: 'high',
onComplete: (user) => console.log('๐ค User fetched:', user)
}
);
queue.addTask(
'Process image',
async () => {
await new Promise(resolve => setTimeout(resolve, 2000));
if (Math.random() > 0.5) throw new Error('Processing failed');
return 'image-processed.jpg';
},
{
priority: 'normal',
maxRetries: 2,
onComplete: (result) => console.log('๐จ Image processed:', result),
onError: (error) => console.log('๐๏ธ Image processing failed:', error.message)
}
);
queue.addTask(
'Send notification',
async () => {
await new Promise(resolve => setTimeout(resolve, 500));
return 'Notification sent';
},
{
priority: 'low',
onComplete: (result) => console.log('๐', result)
}
);
// Add high priority task
setTimeout(() => {
queue.addTask(
'Urgent task',
async () => {
await new Promise(resolve => setTimeout(resolve, 800));
return 'Urgent task completed';
},
{
priority: 'high',
onComplete: (result) => console.log('๐จ', result)
}
);
}, 1500);
๐ Key Takeaways
Youโve mastered arrow functions in TypeScript! Hereโs what you can now do:
- โ Write concise functions with clean, modern syntax ๐ช
- โ
Leverage lexical
this
for predictable behavior ๐ก๏ธ - โ Use implicit returns for single expressions ๐ฏ
- โ Choose wisely between arrow and regular functions ๐
- โ Apply functional patterns with confidence! ๐
Remember: Arrow functions arenโt always better - theyโre different tools for different jobs. Use them wisely! ๐ค
๐ค Next Steps
Congratulations! ๐ Youโve mastered arrow functions in TypeScript!
Hereโs what to do next:
- ๐ป Complete the task queue exercise above
- ๐๏ธ Refactor existing code to use arrow functions appropriately
- ๐ Move on to our next tutorial: Union and Intersection Types
- ๐ Practice using arrow functions in React or Node.js projects!
Remember: The best way to learn is by doing. Start using arrow functions in your code today! ๐
Happy coding! ๐๐โจ