Functions & Generics
Typing functions properly and writing reusable code with generics

Sprout presents
Typed parameters, typed returns, and then generics, which let one function work with many types. Generics are my favorite topic. Sorry in advance.Typed parameters, typed returns, and then generics, which let one function work with many types. Generics are my favorite topic. Sorry in advance.

Ever used a vending machine? Money goes in, you push a button, a specific thing comes out. The rules are dead simple. It takes coins and bills (the input types) and hands back snacks or drinks (the output type). Try feeding it a rock and it spits the rock right back at you.
Typed functions work the same way. You decide EXACTLY what goes in and what comes out, and TypeScript rejects anything that doesn't fit.
Typing Function Parameters#
Every function parameter should get a type annotation.
// Each parameter has a type
function greet(name: string, age: number): string {
return `Hello, ${name}! You are ${age} years old.`;
}
greet("Alice", 28); // OK
greet("Bob", "thirty"); // Error! 'string' is not assignable to 'number'
greet("Charlie"); // Error! Expected 2 arguments, but got 1Optional and Default Parameters#
// Optional parameter (use ?)
function createUser(name: string, email?: string): void {
console.log(`Name: ${name}`);
if (email) {
console.log(`Email: ${email}`);
}
}
createUser("Alice"); // OK
createUser("Bob", "[email protected]"); // OK
// Default parameter (provides a fallback value)
function greetUser(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
greetUser("Alice"); // "Hello, Alice!"
greetUser("Bob", "Hey"); // "Hey, Bob!"Return Types#
The return type goes right after the parameter list, with a colon in front of it.
// Explicitly typed return
function add(a: number, b: number): number {
return a + b;
}
// TypeScript catches wrong return types
function isAdult(age: number): boolean {
return age >= 18; // OK - returns boolean
}
// void means the function returns nothing
function logMessage(message: string): void {
console.log(message);
// No return statement needed
}Arrow Functions with Types#
Arrow functions follow pretty much the same pattern.
// Typed arrow function
const multiply = (x: number, y: number): number => {
return x * y;
};
// Short form for simple returns
const double = (n: number): number => n * 2;
// Function type annotation (variable holds a function)
const formatPrice: (amount: number) => string = (amount) => {
return `$${amount.toFixed(2)}`;
};Function Types#
You can even describe the shape of a whole function with a type.
// Define a function type
type MathOperation = (a: number, b: number) => number;
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
const multiply: MathOperation = (a, b) => a * b;
// Use function types as parameters
function calculate(x: number, y: number, operation: MathOperation): number {
return operation(x, y);
}
calculate(10, 5, add); // 15
calculate(10, 5, subtract); // 5Introduction to Generics#
Okay, deep breath. Generics have a reputation for being the scary part, and they're honestly one of the best features TypeScript has. They let you write functions and types that work with any type while keeping all your type safety.
Picture a box that can hold anything. The trick is that once you put something in it, the box REMEMBERS what's inside.
// Without generics - you lose type information
function getFirst(items: any[]): any {
return items[0];
}
const first = getFirst([1, 2, 3]); // type is 'any' - not helpful
// With generics - type is preserved
function getFirstItem<T>(items: T[]): T {
return items[0];
}
const firstNumber = getFirstItem([1, 2, 3]); // type is 'number'
const firstString = getFirstItem(["a", "b", "c"]); // type is 'string'That <T> is a type parameter. It's a placeholder that gets filled in when you call the function... like a blank on a form that TypeScript fills in based on whatever you pass it.
Generic Functions in Practice#

// A function that wraps any value in an array
function wrapInArray<T>(item: T): T[] {
return [item];
}
wrapInArray(42); // returns number[] = [42]
wrapInArray("hello"); // returns string[] = ["hello"]
// Multiple type parameters
function makePair<A, B>(first: A, second: B): [A, B] {
return [first, second];
}
const pair = makePair("age", 25); // type is [string, number]Generic Constraints#
Sometimes you want a generic with some guardrails on it. Constraints make sure the type parameter has certain properties.
// Constraint: T must have a 'length' property
function getLength<T extends { length: number }>(item: T): number {
return item.length;
}
getLength("hello"); // OK - strings have length
getLength([1, 2, 3]); // OK - arrays have length
getLength(42); // Error! number doesn't have 'length'
// Constraint with interface
interface HasId {
id: number;
}
function findById<T extends HasId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id);
}Real-World Example: API Helper#
Here's where generics really earn their keep in a real app.
interface ApiResponse<T> {
data: T;
error: string | null;
loading: boolean;
}
async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
try {
const response = await fetch(url);
const data: T = await response.json();
return { data, error: null, loading: false };
} catch (err) {
return { data: null as T, error: "Failed to fetch", loading: false };
}
}
// TypeScript knows the exact shape of the response
interface User {
id: number;
name: string;
}
const result = await fetchData<User>("/api/user/1");
// result.data is typed as User
console.log(result.data.name); // TypeScript knows 'name' existsTL;DR#
- Function parameters need type annotations, like
function greet(name: string) - Return types go after the parameters, like
function add(a: number, b: number): number - Use
?for optional parameters and= valuefor defaults - Generics (
<T>) let you write type-safe functions that work with any type - Constraints (
<T extends Something>) put limits on which types a generic will take - Arrow functions get typed the same way regular functions do
What's Next?#
With functions and generics in your back pocket, you can write code that's flexible AND type-safe. Next up is union types, which let a variable be one of a few different types, and type narrowing, which helps TypeScript figure out which one you're actually holding...
This lesson ends with a short activity.
