# Functions and Signatures

> Source: https://learn-typescript.org/functions-and-signatures/
> Part of Learn TypeScript, free to read.

Annotating a function is where types earn the most, because a signature is checked at every place the function is called — not just where it is defined.

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

add(2, 3);        // 5
add("2", 3);      // error at the CALL SITE, where the mistake is
```

## Parameters and return types

```typescript
function greet(name: string): string {
  return `Hello, ${name}`;
}

const double = (n: number): number => n * 2;
```

The return type can usually be inferred, and leaving it off is fine for short functions. Annotate it explicitly when:

- the function is **exported** — it is a contract, and an explicit type stops an accidental change from silently altering it
- the body is long enough that inference is hard for a *reader* to follow
- you want the compiler to catch a wrong return inside the function rather than at its callers

```typescript
export function parsePort(raw: string): number {
  const n = Number(raw);
  return Number.isInteger(n) ? n : 3000;
}
```

## Optional and default parameters

```typescript
function log(message: string, level?: string) {
  console.log(`[${level ?? "info"}] ${message}`);
}

log("started");            // fine — level is undefined
log("failed", "error");
```

`level?: string` means the type is `string | undefined`. A default value does the same job and removes the `undefined`:

```typescript
function log(message: string, level: string = "info") {
  console.log(`[${level}] ${message}`);   // level is string, never undefined
}
```

Optional parameters must come after required ones.

## Rest parameters

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

sum(1, 2, 3);          // 6
sum(...[4, 5, 6]);     // 15
```

## void and undefined

`void` is the return type of a function that returns nothing useful:

```typescript
function notify(message: string): void {
  console.log(message);
}
```

It is subtly different from `undefined`: a `void` return type means "ignore whatever this returns", which is what lets you pass a value-returning function where a void one is expected.

```typescript
const items: string[] = [];
[1, 2, 3].forEach((n) => items.push(String(n)));
// push returns a number; forEach expects void; this is allowed
```

## Function types

You can describe the *shape* of a function, which is how you type callbacks and stored handlers.

```typescript
type Comparator = (a: number, b: number) => number;

const byValue: Comparator = (a, b) => a - b;   // parameters inferred from Comparator
[3, 1, 2].sort(byValue);
```

Note that `byValue` needed no annotations on `a` and `b` — TypeScript infers them from the declared type. This is **contextual typing**, and it is why callbacks usually need no annotations at all:

```typescript
["a", "bb"].map((s) => s.length);      // s is string, inferred from the array
```

## Typing a callback parameter

```typescript
function fetchUser(
  id: string,
  onSuccess: (user: User) => void,
  onError?: (error: Error) => void,
): void {
  // ...
}
```

Writing the callback types out is what makes the caller's arrow function fully typed, with autocomplete on `user` and no annotations needed at the call site.

## Overloads, briefly

Occasionally one function has genuinely different shapes depending on its arguments:

```typescript
function parse(input: string): object;
function parse(input: string, asArray: true): unknown[];
function parse(input: string, asArray?: boolean): object | unknown[] {
  const value = JSON.parse(input);
  return asArray ? [value].flat() : value;
}
```

The first two lines are the signatures callers see; the third is the implementation, which callers cannot call directly. Reach for overloads rarely — a union return type or two separate functions is usually clearer.

## Exercise

```typescript
// Write a function `formatPrice` that:
//   - takes an amount in cents (number) and an optional currency (string, default "USD")
//   - returns a string like "$12.34"
//   - has explicit parameter and return type annotations
// Then write a `Formatter` function type describing its shape, and assign it.

// write your code here
```

## Common questions

### Should I always annotate the return type?

For exported functions, yes — it is a contract, and an explicit annotation means a change to the body cannot silently change what callers receive. For small internal functions, inference is fine and less to maintain.

### Why do my callback parameters not need types?

Contextual typing. When TypeScript already knows the expected function type — from an array method, or from a declared parameter type — it infers the parameter types for you. If your callback parameters are showing as `any`, the surrounding type is missing or too loose.

### What is the difference between `void` and `undefined` as a return type?

`undefined` means the function must actually return `undefined`. `void` means the return value should be ignored, which is more permissive — a function returning a value can be passed where a `void`-returning one is expected. Use `void` for callbacks and handlers.
