Skip to content

Functions

Functions are the heart of any JavaScript application. TypeScript enhances them by letting you explicitly describe:

  • what parameters a function expects
  • what it returns
  • how it behaves in different call scenarios
  • whether it returns nothing (void) or never returns at all (never)

This transforms functions from “flexible but risky” to “precise and self‑documenting.”


Parameter Types

In JavaScript, function parameters are untyped. TypeScript lets you specify exactly what each parameter should be.

function greet(name: string, age: number) {
  return `${name} is ${age} years old`
}

If you call it incorrectly:

greet("Alice", "30") // ❌ Error
greet(123, 456)      // ❌ Error

Optional parameters

Use ? to mark parameters as optional:

function log(message: string, prefix?: string) {
  console.log(prefix ? `${prefix}: ${message}` : message)
}

Default parameters

Default values automatically infer types:

function multiply(a: number, b = 1) {
  return a * b
}

Return Types

TypeScript can infer return types, but explicitly annotating them improves clarity and prevents accidental changes.

function add(a: number, b: number): number {
  return a + b
}

If you accidentally return the wrong type:

function add(a: number, b: number): number {
  return "not a number" // ❌ Error
}

Why explicitly annotate return types?

  • Prevents accidental return type changes
  • Makes refactoring safer
  • Improves readability and API clarity

Function Overloads

JavaScript functions often behave differently depending on the arguments passed. TypeScript lets you model this with overloads.

Example: A function that returns different types

function format(input: string): string
function format(input: number): string
function format(input: string | number): string {
  return `Value: ${input}`
}

Usage:

format("hello") // ✔ OK
format(123)     // ✔ OK
format(true)    // ❌ Error

Why overloads matter

They allow you to express:

  • multiple valid call signatures
  • different return types based on input
  • more precise type checking

This is especially useful for:

  • parsing functions
  • utility libraries
  • DOM APIs
  • data transformation functions

void vs never

These two special return types often confuse newcomers, but they serve very different purposes.


void — A function that returns nothing

function log(message: string): void {
  console.log(message)
}

void means:

  • the function may return undefined
  • callers should not expect a value

It’s the correct type for:

  • logging
  • event handlers
  • callbacks that don’t return anything

never — A function that never returns

A function returns never when it cannot return normally.

1. Functions that always throw

function fail(message: string): never {
  throw new Error(message)
}

2. Functions with infinite loops

function loopForever(): never {
  while (true) {}
}

3. Exhaustiveness checking

never is essential for catching missing cases:

type Shape = { kind: "circle" } | { kind: "square" }

function area(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return 1
    case "square":
      return 2
    default:
      const _exhaustive: never = shape // ❌ Error if a case is missing
      return _exhaustive
  }
}

Summary: void vs never

Type Meaning Example
void Function returns nothing Logging, event handlers
never Function never returns Throws, infinite loops, exhaustiveness checks

Summary

In this lesson, you learned how TypeScript enhances functions with:

1. Parameter types

  • required, optional, and default parameters

2. Return types

  • explicit annotations for clarity and safety

3. Function overloads

  • multiple call signatures for flexible APIs

4. void vs never

  • void → returns nothing
  • never → never returns at all

These tools make your functions predictable, self‑documenting, and far safer than their JavaScript counterparts.