LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 715 min read

Functions in TypeScript

Learn to type function parameters and return values, optional and default parameters, rest parameters, overloads, and arrow function behavior.

Introduction

Functions are where TypeScript's type checking pays off the most — describing exactly what a function expects and what it returns catches an enormous number of bugs at the call site, before code ever runs.

What You Will Learn
  • How to annotate function parameters and return types.
  • The difference between optional and default parameters.
  • How to type rest parameters for variable-length argument lists.
  • How function overloads describe multiple valid call shapes for one function.
  • How arrow functions handle `this` differently from regular functions.

Typing Parameters and Return Values

Each parameter gets a type annotation, and the return type is annotated after the closing parenthesis. In strict mode, TypeScript requires every parameter to have a known type — an untyped parameter is treated as an implicit `any` and flagged as an error.

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

Optional and Default Parameters

A parameter marked with `?` may be omitted by the caller, and is `undefined` inside the function if not provided. A default parameter instead supplies a concrete fallback value automatically used whenever the caller omits (or passes `undefined` for) that argument.

function greet(name: string, greeting?: string): string {
return `${greeting ?? "Hello"}, ${name}!`;
}
function power(base: number, exponent: number = 2): number {
return base ** exponent;
}
console.log(greet("Amol")); // Hello, Amol!
console.log(power(3)); // 9

Rest Parameters

A rest parameter collects any remaining arguments into a typed array, and must always be the last parameter in the list.

function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10

Function Overloads

Overloads let you describe several different valid call shapes for the same function name — different combinations of parameter types that each produce a differently-typed result. You write one or more overload signatures, followed by a single implementation signature that is compatible with all of them.

function makeId(value: string): string;
function makeId(value: number): string;
function makeId(value: string | number): string {
return `ID-${value}`;
}
console.log(makeId(42)); // ID-42
console.log(makeId("abc")); // ID-abc
Output

Click Run to see what this code prints.

Arrow Functions and this

Arrow functions do not have their own `this` — they inherit it lexically from the surrounding scope, at the point they were defined. This makes them the safer default choice for callbacks (like array methods and event handlers), where a regular `function` expression would otherwise silently rebind `this` in confusing ways.

void vs undefined Returns

A return type of `void` signals that a function's return value is meant to be ignored by callers — it's commonly used for callback parameters, like `Array.prototype.forEach`'s callback type. It is subtly different from a return type of `undefined`, which requires the function to explicitly return `undefined` and signals the value matters.

Common Mistakes

Avoid These Mistakes
  • Writing overload signatures that the single implementation signature can't actually satisfy for every listed case.
  • Confusing optional parameters (`b?: number`, can be `undefined` inside the function) with default parameters (`b: number = 0`, always has a concrete value).
  • Placing a rest parameter anywhere except last in the parameter list, which TypeScript will reject.
  • Using a regular `function` expression for a callback where `this` matters, then being surprised `this` isn't what was expected.

Best Practices

  • Let TypeScript infer simple return types, but add explicit return type annotations on exported/public functions to lock down their contract.
  • Prefer default parameters over manual `param === undefined` checks inside a function body.
  • Reserve function overloads for public APIs with a genuinely small number of distinct call shapes — most functions are clearer with a single union parameter type.
  • Use arrow functions for callbacks and class field methods to avoid `this`-binding surprises.

Frequently Asked Questions

Yes — in strict mode, an untyped parameter is treated as an implicit any and flagged as a compile error; every parameter needs a known type, whether explicit or inferable from a default value.

Yes, using a union return type like `string | number`, or by writing overload signatures if the input shape and output shape are linked together.

Marking callback parameters where the caller intentionally ignores whatever value the callback returns, such as the callback type in Array.prototype.forEach.

No — only the single implementation function exists at runtime; overload signatures are purely a compile-time description of the function's public API.

Key Takeaways

  • Function parameters and return values are annotated with types, and strict mode requires every parameter to have a known type.
  • Optional parameters (`?`) may be omitted; default parameters supply a concrete fallback value automatically.
  • Rest parameters collect remaining arguments into a typed array and must come last.
  • Overloads describe multiple valid call shapes for one function, backed by a single compatible implementation.
  • Arrow functions inherit `this` lexically, making them the safer default for callbacks.

Summary

You now know how to type functions thoroughly, from simple parameters through overloads. Next, you'll bring types into object-oriented programming with classes — constructors, access modifiers, inheritance, and more.

Next Lesson →

Classes & OOP