Difficulty: Beginner
Functions are the fundamental building blocks of any TypeScript application, and typing them properly is essential for type safety. Every function parameter should have an explicit type annotation, as TypeScript cannot infer parameter types from the function definition alone. The return type can usually be inferred, but adding it explicitly is good practice for documentation and catching accidental return type changes during refactoring.
TypeScript supports typing both function declarations and arrow functions. For a declaration: `function add(a: number, b: number): number { return a + b; }`. For an arrow function: `const add = (a: number, b: number): number => a + b;`. Both are equivalent in terms of type safety. The return type annotation comes after the parameter list and before the function body.
The `void` return type indicates that a function doesn't return a meaningful value. Functions with side effects like `console.log`, DOM manipulation, or sending analytics events typically return void. Note that `void` is different from `undefined` - a void function can implicitly return undefined, but TypeScript treats them differently in type compatibility. When a callback is typed as returning `void`, the caller promises not to use the return value.
Function types can also be expressed as standalone types for use in variables, parameters, and interfaces. The syntax is `(param: Type) => ReturnType`. For example, `type Formatter = (value: number) => string` describes any function that takes a number and returns a string. This is useful for callback parameters, event handlers, and anywhere you need to describe a function's shape without defining it.
// Function declaration with types
function add(a: number, b: number): number {
return a + b;
}
// Arrow function with types
const multiply = (a: number, b: number): number => a * b;
// Function returning a string
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(add(3, 4));
console.log(multiply(5, 6));
console.log(greet("TypeScript"));
Both function declarations and arrow functions support the same type annotation syntax. Parameters get `: Type` after the name, and the return type goes after the closing parenthesis.
// void: function doesn't return a meaningful value
function logInfo(message: string): void {
console.log(`[INFO] ${message}`);
}
// void in callback types
function executeWithLog(fn: () => void, label: string): void {
console.log(`Starting: ${label}`);
fn();
console.log(`Finished: ${label}`);
}
logInfo("Application started");
executeWithLog(() => {
console.log("Doing work...");
}, "task");
Use `void` for functions that perform side effects without returning data. The callback parameter `fn: () => void` describes a function that takes no arguments and returns nothing.
// Define a function type
type MathOperation = (a: number, b: number) => number;
// Use it to type variables
const subtract: MathOperation = (a, b) => a - b;
const divide: MathOperation = (a, b) => a / b;
// Use it in a higher-order function
function applyOperation(x: number, y: number, op: MathOperation): number {
return op(x, y);
}
console.log(applyOperation(10, 3, subtract));
console.log(applyOperation(20, 4, divide));
console.log(applyOperation(7, 8, (a, b) => a * b));
Function type expressions describe the shape of a function. When you assign a function to a variable typed with a function type, TypeScript infers the parameter types from the type expression - you don't need to annotate them again.
Parameter types, Return types, Arrow functions, void return type, Function type expressions