Functions in TypeScript

Learn how to type function parameters, return values, optional arguments, and callbacks in TypeScript. Strongly typed functions improve reliability, refactoring safety, and architectural clarity.

On this page

Why typing functions matters

Functions are the core building blocks of any application. In production systems, incorrect function usage is one of the most common sources of bugs. TypeScript allows you to define precise contracts for inputs and outputs, making refactoring safer and collaboration clearer.

Typing function parameters

Always define explicit types for function parameters. This prevents incorrect usage and improves editor tooling.

function greet(name: string): string {
  return "Hello " + name;
}

greet("Ozan");   // OK
greet(123);      // Type error

Typing return values

Although TypeScript can infer return types, explicitly defining them is recommended for exported functions and shared modules.

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

Explicit return types protect you during internal refactoring.

Optional parameters

Use ? to define optional parameters. These parameters may be undefined and must be handled safely.

function createUser(name: string, age?: number): string {
  if (age) {
    return name + " is " + age;
  }
  return name;
}

Default parameters

Default parameters provide safe fallback values while maintaining strong typing.

function log(message: string, level: string = "info"): void {
  console.log(level + ": " + message);
}

Rest parameters

Rest parameters allow variable-length arguments with type safety.

function sum(...numbers: number[]): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

Function type expressions

You can define function types explicitly and reuse them across your codebase.

type MathOperation = (a: number, b: number) => number;

const multiply: MathOperation = (a, b) => a * b;

This improves consistency and reduces duplication.

Callbacks and higher-order functions

Functions that receive other functions should define callback types clearly.

function processUser(id: number, callback: (id: number) => void): void {
  callback(id);
}

Clear callback types prevent incorrect assumptions about parameters.

Void and never return types

void represents functions that do not return a meaningful value. never represents functions that never successfully complete, such as those that throw errors.

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

Overloads

Function overloads allow multiple call signatures for a single implementation.

function format(value: number): string;
function format(value: string): string;
function format(value: number | string): string {
  return value.toString();
}

Overloads are useful for flexible APIs with predictable contracts.

Functions as architecture boundaries

In large applications, functions represent boundaries between layers: controllers, services, repositories, utilities. Typing these boundaries explicitly improves long-term maintainability.

Common mistakes

  • Forgetting to type exported functions.
  • Overusing any in callback definitions.
  • Ignoring optional parameter undefined checks.
  • Creating overly complex overloads instead of simplifying the API.

Production guidance

  • Always type function parameters explicitly.
  • Prefer explicit return types for shared modules.
  • Use reusable function type aliases for consistency.
  • Keep overloads simple and predictable.

What’s next

Now that you understand how to type functions safely, the next step is learning about enums and structured constant modeling in TypeScript.