TSTypeScript · Lesson 4 of 10

Typed Functions

Adding types to functions is where TypeScript pays off most immediately — your editor knows what you're passing and what you'll get back, and the compiler catches mismatches before you run anything.

TypeScript
// Function declarations — parameter and return types
function add(a: number, b: number): number {
  return a + b
}

// Arrow functions
const multiply = (a: number, b: number): number => a * b

// Optional parameters — must come after required ones
function greet(name: string, greeting?: string): string {
  return `${greeting ?? "Hello"}, ${name}!`
}

// Default parameters
function createUser(name: string, role: string = "user", active: boolean = true) {
  return { name, role, active }
}

// Rest parameters
function sum(...nums: number[]): number {
  return nums.reduce((acc, n) => acc + n, 0)
}

console.log(sum(1, 2, 3, 4, 5))  // 15

// Function overloads — multiple signatures for one implementation
function format(value: string): string
function format(value: number): string
function format(value: string | number): string {
  if (typeof value === "number") return value.toFixed(2)
  return value.trim()
}

console.log(format(3.14159))  // "3.14"
console.log(format("  hello  "))  // "hello"
TypeScript
// Higher-order functions with types
function map<T, U>(arr: T[], fn: (item: T) => U): U[] {
  return arr.map(fn)
}

const doubled = map([1, 2, 3], n => n * 2)           // number[]
const strings = map([1, 2, 3], n => n.toString())     // string[]

// Callback types
type Predicate<T> = (item: T) => boolean
type Transformer<T, U> = (item: T) => U

function filter<T>(arr: T[], pred: Predicate<T>): T[] {
  return arr.filter(pred)
}

// Type narrowing — TypeScript learns the type from checks
function processValue(val: string | number | null): string {
  if (val === null) return "null"
  if (typeof val === "number") return val.toFixed(2)
  return val.toUpperCase()  // TypeScript knows val is string here
}

// User-defined type guards
interface Cat { meow(): void }
interface Dog { bark(): void }

function isCat(animal: Cat | Dog): animal is Cat {
  return "meow" in animal
}

function makeSound(animal: Cat | Dog): void {
  if (isCat(animal)) {
    animal.meow()   // TypeScript knows it's a Cat
  } else {
    animal.bark()   // TypeScript knows it's a Dog
  }
}
✦ Tip
Always annotate function return types explicitly on public API functions — even though TypeScript can infer them. Explicit return types act as a contract: if you accidentally return the wrong type, TypeScript flags the function body, not the caller.