Menu

TypeScript Function: Parameter and Return Types, void

How to type functions in TypeScript: annotate every parameter, annotate or infer the return type, use void for functions that return nothing, and type arrow functions, function expressions, async functions and the this parameter.

This page includes runnable editors - edit, run, and see output instantly.

A TypeScript function is a JavaScript function with types on its parameters and, optionally, on its return value. Every parameter gets an annotation, the return type goes after the parameter list, and the compiler checks every call against both.

Both bad calls are compile-time errors (TS2345 and TS2554). The // @ts-expect-error comments tell the compiler that an error is expected on the next line, so the rest of the file still runs. Remove one of them and run again to see the real message.

Parameter Types

Each parameter is written name: Type. Under strict, which is on by default in TypeScript 7, a parameter with no annotation and no context to infer from is an error: Parameter 'x' implicitly has an 'any' type. (TS7006). So in practice every parameter of a standalone function is annotated.

Any type works as a parameter type: primitives, arrays, object types, unions, other functions.

TypeScript checks the number of arguments too. Passing more or fewer than the function declares is an error, unlike plain JavaScript, which fills missing ones with undefined and ignores extras. To make a parameter optional or give it a default, see optional parameters.

Return Types: Annotate or Infer

The return type follows the closing parenthesis: function f(): Type. It is optional. Without it, TypeScript infers the type from every return in the body.

An annotation earns its place when the function is exported, when it has several return paths, or when it is recursive. The error then shows up inside the function, at the wrong return, instead of at a caller. A classic case is a branch that forgets to return:

The compiler reports index.ts(2,32): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. Add a final return "C"; and it runs. Without the : string annotation the function would compile with the inferred type "A" | "B" | undefined, and the problem would move to whoever uses the result.

void: Functions That Return Nothing

A function that only does something (logs, writes, mutates) has the return type void. It is what TypeScript infers when there is no return with a value, and you can write it explicitly.

These rules about void surprise people:

SituationAllowed?
function f(): void { return 42; }No: Type 'number' is not assignable to type 'void'. (TS2322)
const f: () => void = () => 42;Yes: the value is returned but callers must not rely on it
function f(): undefined {}Yes (TypeScript 5.1 and later)
Using the result of a void function in an ifNo: An expression of type 'void' cannot be tested for truthiness. (TS1345)

The second row is deliberate. It is why arr.forEach(x => list.push(x)) compiles even though push returns a number. void in a function type means "whatever this returns, nobody reads it". For a function that never returns at all (it always throws or loops forever), the return type is never.

Arrow Functions and Function Expressions

Arrow functions and function expressions take the same annotations. The return type goes after the parameter list, before =>.

Without the parentheses, (x, y) => { x, y } is a block with no return, and the function returns undefined. The runtime rules for arrow functions (no own this, no arguments) are the same as in JavaScript; see arrow functions in JavaScript.

When a function is written inline as an argument, you usually do not annotate its parameters at all. TypeScript knows what the callback receives from the function it is passed to (contextual typing): in [1, 2].map(n => n * 2), n is already number. Writing the type of a function as a value, (n: number) => string, is covered in function types.

Returning Several Values

A function returns one value, so return an object or a tuple and destructure it.

The tuple return type needs the annotation. Without it, [a, b] is inferred as string[]: destructuring still works, but the type no longer says there are exactly two elements.

Async Functions

An async function always returns a Promise. Annotate the return type as Promise<T>, where T is what the function returns inside.

async function loadScore(id: number): Promise<number> {
  await new Promise((resolve) => setTimeout(resolve, 10));
  return id * 10;
}

async function main(): Promise<void> {
  const score = await loadScore(4); // score: number
  console.log("score", score);
}

main();

Writing async function f(): number is an error: The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'? (TS1064).

The this Parameter

JavaScript decides this at call time. TypeScript lets you declare what this must be with a fake first parameter named this. It is removed from the compiled JavaScript and callers do not pass it.

interface Counter {
  count: number;
}

function increment(this: Counter, by: number): void {
  this.count += by;
}

const c = { count: 0, increment };
c.increment(2); // fine: this is c

increment(2);
// error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Counter'.

Without the this parameter, this inside a standalone function is an implicit any and strict mode reports 'this' implicitly has type 'any' because it does not have a type annotation. (TS2683). In class methods this is already typed as the class instance, so the parameter is mostly useful for standalone functions attached to objects and for callbacks that libraries call with a specific this.

Frequently Asked Questions

How do you specify a function return type in TypeScript?

Write a colon and the type after the parameter list: function total(a: number, b: number): number { ... }. For an arrow function it goes in the same place: const total = (a: number, b: number): number => a + b;. If you leave it out, TypeScript infers the return type from the return statements.

Should I always annotate the return type in TypeScript?

It is optional, because the return type is inferred. Annotate it on exported functions and on functions with several return paths: the annotation documents the contract, and a wrong return is then reported inside the function instead of at some caller far away.

What is the difference between void and undefined in TypeScript?

void means "the caller should not use the return value". undefined is a concrete value type. A function declared (): void may not return a value, but a callback type () => void accepts a function that does return something, and the result is simply ignored. Use void for functions that return nothing and undefined only when callers really compare the result with undefined.

How do I return multiple values from a TypeScript function?

Return an object ({ min: number; max: number }) or a tuple ([number, number]) and destructure it at the call site. Objects are clearer when the values have different meanings; tuples read well for short pairs such as [value, setValue].

What is the return type of an async function in TypeScript?

Always a Promise. An async function that returns a number has the return type Promise<number>, and writing : number on it is a compile error (TS1064). A function that returns nothing is Promise<void>.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED