Functions & Arrow Functions
Reusable Recipes of Code
Open interactive version (quiz + challenge)Real-world analogy
A function is like a vending machine — you press a button (pass arguments), gears turn inside (logic runs), and a snack drops out (return value). Arrow functions are the sleek, touchscreen version of the same machine. Same snacks, less bulk!
What is it?
Functions are reusable blocks of code. They take input (parameters), do work, and return output. TypeScript requires you to specify parameter types and return types. Arrow functions are shorter syntax for the same thing.
Real-world relevance
Almost every function you write will be an arrow function. They're so common in React (hooks, event handlers) that you'll type => a hundred times a day!
Key points
- Function Declarations — The traditional way to define functions: function add(a: number, b: number): number { return a + b; }. Function declarations are hoisted, meaning you can call them before they appear in the code. They are verbose but very clear and readable for beginners learning the basics.
- Arrow Functions — The Modern Way — The modern shorthand: const add = (a: number, b: number): number => a + b. Arrow functions are shorter and used everywhere in modern JavaScript and React. If the body is a single expression, you can skip the curly braces and return keyword. They also do not bind their own 'this' context.
- Optional & Default Parameters — function greet(name: string, title?: string) {} means title is optional — callers can skip it. Use defaults for common values: function greet(name: string, greeting = 'Hello') {} means greeting defaults to 'Hello' if not provided. This reduces boilerplate and makes your API flexible.
- Rest Parameters (...args) — The three dots collect ALL remaining arguments into an array: function sum(...numbers: number[]): number {}. Call it with sum(1,2,3,4,5) and numbers becomes [1,2,3,4,5]. This is flexible because you can pass any number of arguments without defining each one separately in the signature.
- Function Types — type MathFn = (a: number, b: number) => number; describes a function's shape — its parameters and return type. You can use this type to ensure variables and parameters hold functions with the correct signature: const add: MathFn = (a,b) => a+b. TypeScript verifies the assignment matches.
- Callbacks — Passing Functions — A callback is a function passed as an argument to another function: function doMath(a: number, b: number, operation: MathFn) {}. The receiving function calls it back at the right time. Callbacks are the foundation of event handlers, array methods like map and filter, and asynchronous patterns.
- Function Overloads — function format(value: string): string; function format(value: number): string; — same function name with different parameter types. TypeScript picks the right overload automatically based on what you pass, giving you precise return types for each call. Useful for utility functions that accept multiple input types.
- Closures — Functions with Memory — A closure is a function that remembers variables from its outer scope even after that scope has finished executing. For example, a counter function that returns an increment function — the inner function closes over the count variable and keeps it alive between calls. Closures power many JavaScript patterns.
- Pure Functions & Side Effects — A pure function always returns the same output for the same input and changes nothing outside itself. Functions that modify global state, write to a database, or log to the console have side effects. Keeping functions pure makes your code predictable, testable, and easier to debug in large applications.
Code example
// Named function — traditional way
function add(a: number, b: number): number {
return a + b;
}
// Arrow function — full version (pick ONE style)
const addArrow = (a: number, b: number): number => {
return a + b;
};
// Arrow function — short version (one line!)
const addShort = (a: number, b: number) => a + b;
// No parameters
const sayHi = () => "Hi!";
// One parameter (parentheses optional)
const double = (x: number) => x * 2;
// Multiple lines with arrow function
const greetArrow = (name: string) => {
console.log("Hello " + name);
return `Welcome, ${name}!`;
};
// Optional parameter (each example below is standalone)
function greetWithTitle(name: string, title?: string) {
if (title) {
return `Hello ${title} ${name}`;
}
return `Hello ${name}`;
}
greetWithTitle("John"); // "Hello John"
greetWithTitle("John", "Mr."); // "Hello Mr. John"
// Default parameter
function greetDefault(name: string, greeting: string = "Hello") {
return `${greeting} ${name}`;
}
greetDefault("John"); // "Hello John"
greetDefault("John", "Hey"); // "Hey John"
// Rest parameters — any number of arguments!
function sum(...numbers: number[]): number {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
}
sum(1, 2); // 3
sum(1, 2, 3, 4, 5); // 15
// Function type
type MathFn = (a: number, b: number) => number;
const add: MathFn = (a, b) => a + b;
const multiply: MathFn = (a, b) => a * b;
// Callback — pass function as parameter
function doMath(a: number, b: number, operation: MathFn): number {
return operation(a, b);
}
doMath(5, 3, add); // 8
doMath(5, 3, multiply); // 15
// Function overloads
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
format("hello"); // "HELLO"
format(3.14159); // "3.14"Line-by-line walkthrough
- 1. Named function — traditional way
- 2. Declaring a function
- 3. Returning a value
- 4. Closing block
- 5.
- 6. Arrow function — full version
- 7. Declaring a variable
- 8. Returning a value
- 9. Closing block
- 10.
- 11. Arrow function — short version (one line!)
- 12. Declaring a variable
- 13.
- 14. No parameters
- 15. Declaring a variable
- 16.
- 17. One parameter (parentheses optional)
- 18. Declaring a variable
- 19.
- 20. Multiple lines with arrow function
- 21. Declaring a variable
- 22. Printing output to the console
- 23. Returning a value
- 24. Closing block
- 25.
- 26. Optional parameter
- 27. Declaring a function
- 28. Conditional check
- 29. Returning a value
- 30. Closing block
- 31. Returning a value
- 32. Closing block
- 33.
- 34.
- 35.
- 36. Default parameter
- 37. Declaring a function
- 38. Returning a value
- 39. Closing block
- 40.
- 41.
- 42.
- 43. Rest parameters — any number of arguments!
- 44. Declaring a function
- 45. Declaring a variable
- 46. Loop iteration
- 47.
- 48. Closing block
- 49. Returning a value
- 50. Closing block
- 51.
- 52.
- 53.
- 54. Function type
- 55. Defining a type alias
- 56. Declaring a variable
- 57. Declaring a variable
- 58.
- 59. Callback — pass function as parameter
- 60. Declaring a function
- 61. Returning a value
- 62. Closing block
- 63.
- 64.
- 65.
- 66. Function overloads
- 67. Declaring a function
- 68. Declaring a function
- 69. Declaring a function
- 70. Conditional check
- 71. Returning a value
- 72. Closing block
- 73. Returning a value
- 74. Closing block
- 75.
- 76.
Spot the bug
const add = (a: number, b: number): string => {
return a + b;
};Need a hint?
Look at what the function actually returns versus its declared return type...
Show answer
The function returns a + b (number + number = number), but the return type is declared as string. Fix: change the return type from 'string' to 'number'.
Explain like I'm 5
A function is like a recipe card. It says: 'Give me flour and eggs, and I'll give you a cake.' You write the recipe once, then use it as many times as you want! Arrow functions are just a shorter way to write the same recipe.
Fun fact
Arrow functions don't have their own 'this' binding. This is confusing at first but makes them perfect for passing to React components!
Hands-on challenge
Write an arrow function that takes an array of numbers and returns the average. Test with [10, 20, 30]!
More resources
- MDN Arrow Functions (MDN Web Docs)
- TypeScript Handbook - Functions (TypeScript Official)
- Arrow Functions Tutorial (Web Dev Simplified)