4. Functions

Functions are a fundamental part of any programming language, and TypeScript enhances their utility by providing a robust type system for parameters and return values. In this article, we’ll explore how to work with functions in TypeScript, including declaring functions with parameter types, specifying return types, and handling optional and default parameters.

Declaring Functions with Parameter Types

In TypeScript, you can declare function parameters with explicit types to ensure that functions receive the correct types of arguments. Here’s an example:

function greet(name: string) {
    console.log(`Hello, ${name}!`);

greet("Alice"); // Correct
greet(42);      // Error: Argument of type '42' is not assignable to parameter of type 'string'.

In the greet function, we’ve specified that the name parameter should be of type string. When we call greet("Alice"), it works correctly because we pass a string argument. However, when we try to pass a number (greet(42)), TypeScript raises an error due to a type mismatch.

Specifying Return Types

You can also specify the return type of a function using : returnType. This helps ensure that functions return values of the expected type:

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

const result: number = add(2, 3); // Correct
const wrongResult: string = add(2, 3); // Error: Type 'number' is not assignable to type 'string'.

In the add function, we’ve explicitly defined the return type as number. This guarantees that the result of calling add is of type number. Attempting to assign the result to a variable of the wrong type, as shown in wrongResult, triggers a TypeScript error.

Optional and Default Parameters

TypeScript supports optional and default function parameters. Optional parameters are defined by appending ? to the parameter name, while default parameters are specified with an equal sign (=) and a default value:

function greetPerson(name: string, age?: number) {
    if (age === undefined) {
        console.log(`Hello, ${name}!`);
    } else {
        console.log(`Hello, ${name} (${age} years old)!`);

greetPerson("Alice"); // Output: Hello, Alice!
greetPerson("Bob", 30); // Output: Hello, Bob (30 years old)!

function sayHello(message: string = "Hello, World!") {

sayHello(); // Output: Hello, World!
sayHello("Bonjour!"); // Output: Bonjour!

In the greetPerson function, age is an optional parameter. If it’s not provided, TypeScript considers it as undefined. In the sayHello function, message is a parameter with a default value of “Hello, World!”.

Arrow Functions and Function Overloading in TypeScript

Arrow functions and function overloading are powerful features in TypeScript that enable developers to write concise and type-safe code. In this article, we will explore how to work with arrow functions and leverage function overloading to create versatile functions.

Arrow Functions

Arrow functions, also known as fat arrow functions, provide a concise and more readable way to define functions in TypeScript. They are especially useful for defining anonymous functions and simplifying the syntax.

Here’s a basic example of an arrow function:

const add = (a: number, b: number): number => a + b;

console.log(add(2, 3)); // Output: 5

In this example, add is an arrow function that takes two parameters (a and b) and returns their sum. Arrow functions automatically capture the surrounding this value, which is useful in certain contexts, such as callback functions.

Function Overloading

Function overloading allows you to define multiple function signatures for the same function name. TypeScript uses the function signature to determine which overloaded version of the function to call based on the provided arguments.

Here’s an example of function overloading:

function greet(person: string): void;
function greet(person: string, age: number): void;

function greet(person: string, age?: number): void {
    if (age === undefined) {
        console.log(`Hello, ${person}!`);
    } else {
        console.log(`Hello, ${person} (${age} years old)!`);

greet("Alice"); // Output: Hello, Alice!
greet("Bob", 30); // Output: Hello, Bob (30 years old)!

In this example, we have two function signatures for the greet function. The first signature takes only a person parameter, and the second signature takes both person and age parameters. TypeScript uses the function signature to determine which version of greet to call based on the provided arguments.

Benefits of Function Overloading

Function overloading in TypeScript offers several benefits:

  1. Type Safety: Function overloading ensures that the correct number and types of arguments are provided to a function, reducing runtime errors.

  2. Improved Readability: Overloaded functions provide clear and self-documenting APIs, making the code more understandable.

  3. Consistency: Developers can call the same function name with different argument combinations, promoting code consistency.

  4. Versatility: Overloaded functions can handle a variety of use cases without the need for multiple function names.


TypeScript’s type system for functions, including parameter types, return types, and optional/default parameters, enhances code quality and helps catch potential errors at compile-time. By explicitly defining types and using optional/default parameters, you can write more robust and self-documenting code, improving both development and maintenance processes.

Arrow functions and function overloading are essential tools in the TypeScript developer’s toolkit. Arrow functions simplify function declarations, making code more concise and readable. Function overloading, on the other hand, enhances type safety and provides versatile and self-documenting APIs for functions with multiple argument combinations. These features contribute to the overall robustness and maintainability of TypeScript codebases.