How to Type a Function in TypeScript

What you’ll build or solve

You’ll type functions inline for everyday code and define reusable function signatures for shared patterns. By the end, you’ll know which approach to use and how to handle common parameter shapes like optional and rest arguments.

When this approach works best

This method works best when:

  • You want to prevent incorrect arguments from reaching your function.
  • You need predictable return types across a codebase.
  • You reuse the same function signature in multiple places.

Skip this if you are writing throwaway scripts and do not care about type safety.

Prerequisites

  • TypeScript installed
  • Basic understanding of JavaScript functions

No special tooling is required beyond TypeScript.


Step-by-step instructions

Step 1: Type functions inline

This is the most common approach. You type parameters and the return value directly on the function.

Add parameter and return types

function greet(name: string): string {

return Hello, ${name};

}

TypeScript now enforces:

  • name must be a string
  • the function must return a string

If you pass the wrong argument type, you get a compile-time error.

greet(123);

Parameter variations you’ll use often

Optional parameter:

function welcome(name?: string): string {

if (!name) return "Welcome guest";

return Welcome ${name};

}

Default value:

function welcome(name: string = "guest"): string {

return Welcome ${name};

}

Rest parameters:

function sum(...numbers: number[]): number {

return numbers.reduce((total, n) => total + n, 0);

}

Arrow functions follow the same typing rules:

const add = (a: number, b: number): number => a + b;

What to look for:

  • Optional parameters must come after required ones.
  • Rest parameters are typed as arrays, like number[].
  • If TypeScript flags a return type error, one branch likely returns the wrong type.

Step 2: Define reusable function types

Use this approach when you repeat the same function signature across multiple functions, callbacks, or config objects.

Option A: Use a type alias

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

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

const subtract: Calculator = (a, b) => a - b;

Option B: Use an interface

interface Formatter {

(value: string): string;

}

const uppercase: Formatter = (value) => value.toUpperCase();

What to look for:

  • TypeScript enforces the same signature everywhere you apply the type.
  • If you change the signature, TypeScript points to all affected call sites.

Examples you can copy

Example 1: Validate an object parameter

interface LoginData {

email: string;

password: string;

}

function login(data: LoginData): boolean {

return data.email.length > 0 && data.password.length > 0;

}


Example 2: Use a union parameter safely

function formatId(id: string | number): string {

return id.toString();

}


Example 3: Type a callback

type Callback = (message: string) => void;

function notify(callback: Callback) {

callback("Task completed");

}


Common mistakes and how to fix them

Mistake 1: Returning different types in different branches

What someone might do:

function getValue(flag: boolean): string {

if (flag) return "Yes";

return 0;

}

Why it breaks:

The function promises a string but sometimes returns a number.

Correct approach:

function getValue(flag: boolean): string {

if (flag) return "Yes";

return "No";

}


Mistake 2: Putting an optional parameter before a required one

What someone might do:

function buildLabel(prefix?: string, value: number): string {

return ${prefix}${value};

}

Why it breaks:

Optional parameters must come after required parameters.

Correct approach:

function buildLabel(value: number, prefix?: string): string {

return ${prefix ?? ""}${value};

}


Mistake 3: Using any to avoid errors

What someone might do:

function process(data: any): any {

return data;

}

Why it breaks safety:

any turns off type checking.

Correct approach:

Use a real type, or use unknown and narrow it:

function process(data: unknown): string {

if (typeof data === "string") return data;

return "Not a string";

}


Troubleshooting

If TypeScript says an argument is not assignable, check parameter order and types.

If you get a return type error, check every return path, especially inside if statements.

If your rest parameter errors mention arrays, confirm you typed it like ...args: string[].

If errors seem confusing, add explicit return types and rerun the compiler.


Quick recap

  • Type functions inline by adding parameter and return types.
  • Use ?, defaults, and rest parameters inside the same inline approach.
  • Arrow functions use the same typing rules.
  • Define reusable function types with type or interface when signatures repeat.