Functions in TypeScript
Why typing functions matters
Functions are the core building blocks of any application. In production systems, incorrect function usage is one of the most common sources of bugs. TypeScript allows you to define precise contracts for inputs and outputs, making refactoring safer and collaboration clearer.
Typing function parameters
Always define explicit types for function parameters. This prevents incorrect usage and improves editor tooling.
function greet(name: string): string {
return "Hello " + name;
}
greet("Ozan"); // OK
greet(123); // Type error
Typing return values
Although TypeScript can infer return types, explicitly defining them is recommended for exported functions and shared modules.
function add(a: number, b: number): number {
return a + b;
}
Explicit return types protect you during internal refactoring.
Optional parameters
Use ? to define optional parameters. These parameters may be undefined and must be handled safely.
function createUser(name: string, age?: number): string {
if (age) {
return name + " is " + age;
}
return name;
}
Default parameters
Default parameters provide safe fallback values while maintaining strong typing.
function log(message: string, level: string = "info"): void {
console.log(level + ": " + message);
}
Rest parameters
Rest parameters allow variable-length arguments with type safety.
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
Function type expressions
You can define function types explicitly and reuse them across your codebase.
type MathOperation = (a: number, b: number) => number; const multiply: MathOperation = (a, b) => a * b;
This improves consistency and reduces duplication.
Callbacks and higher-order functions
Functions that receive other functions should define callback types clearly.
function processUser(id: number, callback: (id: number) => void): void {
callback(id);
}
Clear callback types prevent incorrect assumptions about parameters.
Void and never return types
void represents functions that do not return a meaningful value. never represents functions that never successfully complete, such as those that throw errors.
function fail(message: string): never {
throw new Error(message);
}
Overloads
Function overloads allow multiple call signatures for a single implementation.
function format(value: number): string;
function format(value: string): string;
function format(value: number | string): string {
return value.toString();
}
Overloads are useful for flexible APIs with predictable contracts.
Functions as architecture boundaries
In large applications, functions represent boundaries between layers: controllers, services, repositories, utilities. Typing these boundaries explicitly improves long-term maintainability.
Common mistakes
- Forgetting to type exported functions.
- Overusing any in callback definitions.
- Ignoring optional parameter undefined checks.
- Creating overly complex overloads instead of simplifying the API.
Production guidance
- Always type function parameters explicitly.
- Prefer explicit return types for shared modules.
- Use reusable function type aliases for consistency.
- Keep overloads simple and predictable.
What’s next
Now that you understand how to type functions safely, the next step is learning about enums and structured constant modeling in TypeScript.