More on Functions

Functions are values in JavaScript: you pass them as callbacks, store them in objects and call them with a borrowed this (Functions). TypeScript gives each of those roles a type.

Ways to type a function
Syntax Example Use it for
Function type expression (value: number) => string Callbacks, function variables
Call signature { (s: string): boolean; message: string } Callable objects with properties
Construct signature new (x: number) => Point Classes passed as values
Optional, default, rest (p?: string, g = "Hi", ...n: number[]) Arguments callers may omit
Type parameter <T>(items: T[]) => T Relating inputs to outputs
this parameter function (this: Counter) Functions that rely on this
Function types and call signatures

A function type expression looks like an arrow function with types. The parameter name is required but plays no part in compatibility. When a function also carries properties, write a call signature inside an object type (note the colon instead of the arrow); prefix it with new for a construct signature, the type of a class.

A function type expression and a call signatureTypeScript
type Formatter = (value: number) => string;
type Validator = {
  message: string;                                  // a property...
  (input: string): boolean;                         // ...plus a call signature
};
const euro: Formatter = (v) => `€${v.toFixed(2)}`;  // v: number from context
function isEmail(s: string) { return s.includes("@"); }
isEmail.message = "Enter a valid email address";    // expando property
const check: Validator = isEmail;                   // OK
const bad: Formatter = (v: string) => v;
Output
sigs.ts(11,7): error TS2322: Type '(v: string) => string' is not assignable to type
  'Formatter'.
  Types of parameters 'v' and 'value' are incompatible.
    Type 'number' is not assignable to type 'string'.

Property assignments on a function declaration become part of its type, so isEmail satisfies Validator. Avoid the global type Function: calling it returns any.

Optional, default and rest parameters

JavaScript lets callers omit any argument (Parameters); TypeScript does not unless you say so. punct?: string has the type string | undefined, whereas a default value makes the parameter optional and keeps it a plain string in the body. A rest parameter has an array type, and spreading into a fixed parameter list needs a tuple, which as const produces.

Optional, default and rest parameters, spreads and voidJavaScript
function greet(name: string, greeting = "Hello", punct?: string): string {
  return `${greeting}, ${name}${punct ?? "!"}`;     // punct: string | undefined
}
function sum(first: number, ...rest: number[]): number {
  return rest.reduce((acc, n) => acc + n, first);
}
const pair = [8, 5] as const;                       // readonly [8, 5]
const loose = [8, 5];                               // number[]
console.log(greet("Ada"), sum(1, 2, 3), Math.atan2(...pair).toFixed(3));
greet();
Math.atan2(...loose);
const lengths: number[] = [];
["a", "bb"].forEach((s) => lengths.push(s.length)); // push returns a number: OK
function reset(): void { return lengths.length; }
Output
params.ts(11,1): error TS2554: Expected 1-3 arguments, but got 0.
params.ts(12,12): error TS2556: A spread argument must either have a tuple type or be passed
  to a rest
parameter.
params.ts(16,26): error TS2322: Type 'number' is not assignable to type 'void'.

Without the error lines the program prints Hello, Ada! 6 1.012. The last two lines show a deliberate asymmetry: a contextual void return type, like the callback type of forEach, means "the result is ignored", so returning push's number is fine; a function declared : void must not return a value.

Generic functions

When the output type depends on the input type, add a type parameter. TypeScript infers it at each call, and a constraint such as T extends { length: number } limits what callers may pass while letting the body use .length. Use as few type parameters as possible; one that appears only once in the signature is probably unnecessary. Generics covers generics in depth.

Inferred type arguments and a constraintJavaScript
function first<T>(items: T[]): T | undefined { return items[0]; }
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}
const n = first([3, 1, 2]);             // number | undefined
const word = longest("tea", "coffee");  // T is string
longest(10, 20);
Output
generic.ts(7,9): error TS2345: Argument of type 'number' is not assignable to parameter of type
'{ length: number; }'.
Overloads

Some functions accept genuinely different argument lists. Write two or more overload signatures followed by one implementation signature whose body handles every case. Callers see only the overloads.

Three overloads over one hidden implementationJavaScript
function parseDate(timestamp: number): Date;
function parseDate(year: number, month: number, day: number): Date;
function parseDate(iso: string): Date;
function parseDate(a: number | string, month?: number, day?: number): Date {
  if (typeof a === "string") return new Date(a);
  return month !== undefined && day !== undefined ? new Date(a, month - 1, day) : new Date(a);
}
console.log(parseDate(2026, 9, 15).getMonth());     // 8 (months count from 0)
parseDate(2026, 9);
declare const input: number | string;
parseDate(input);
Output
overload.ts(10,1): error TS2575: No overload expects 2 arguments, but overloads do exist that
  expect either
1 or 3 arguments.
overload.ts(12,11): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'string | number' is not assignable to parameter of type 'string'.
      Type 'number' is not assignable to type 'string'.

The second error is the classic pitfall: each call is checked against one overload at a time, so a number | string argument matches none of them although the implementation would accept it. Prefer a union parameter when the result type is the same; keep overloads for when arguments decide the result type, as in createElement, which returns HTMLCanvasElement for "canvas" but HTMLElement for an unknown tag.

Declaring this

In a function (not an arrow), this depends on how the function is called (prototype, this and new). Declare its type as a fake first parameter named this: it is erased from the JavaScript output, and every call site is checked. Under noImplicitThis, part of strict, an untyped this outside a method is an error.

A this parameter, a detached method and a typed event handlerCSS
interface Counter { count: number; step: number }
function increment(this: Counter, times = 1): number {
  this.count += this.step * times;
  return this.count;
}
const counter = { count: 0, step: 5, increment };
counter.increment(2);                        // OK: this is counter
const detached = counter.increment;
detached();
document.querySelector("button")?.addEventListener("click", function () {
  this.disabled = true;                      // this: HTMLButtonElement
});
function legacy() { return this.count; }
Output
this.ts(9,1): error TS2684: The 'this' context of type 'void' is not assignable to method's
  'this' of type
'Counter'.
this.ts(14,28): error TS2683: 'this' implicitly has type 'any' because it does not have a type
  annotation.

The DOM library declares listeners with a this: HTMLButtonElement parameter, so a function handler gets a typed this for free. An arrow function captures the surrounding this instead.