Home » TypeScript Functions: A Comprehensive Tutorial with Code Examples

TypeScript Functions: A Comprehensive Tutorial with Code Examples

Functions are a core concept in TypeScript, just as in JavaScript, but TypeScript enhances functions by providing static types and advanced type features like type inference, overloads, and more.

In this tutorial, we’ll cover TypeScript functions in depth and provide several examples to illustrate different aspects of function types.

1. Basic Function Syntax

In TypeScript, you can define functions similarly to how you would in JavaScript, but with optional type annotations for parameters and return types.

Example 1: Basic Function with Types

function add(x: number, y: number): number {
    return x + y;
}

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

Here, x and y are parameters of type number, and the function is expected to return a value of type number. If you omit the return type, TypeScript infers it automatically based on the return value.

2. Function Parameters

You can explicitly define types for function parameters to ensure type safety, which is one of TypeScript’s key features.

Example 2: Function Parameter Types

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

console.log(greet("John")); // Output: Hello, John!

In this example, the greet function takes a single parameter name of type string and returns a string.

3. Optional and Default Parameters

TypeScript allows you to define optional and default parameters in functions. Optional parameters are denoted by a question mark ?, while default parameters are assigned default values.

Example 3: Optional and Default Parameters

function logMessage(message: string, user?: string): void {
    if (user) {
        console.log(`${user}: ${message}`);
    } else {
        console.log(message);
    }
}

function greetUser(name: string, greeting: string = "Hello"): string {
    return `${greeting}, ${name}`;
}

logMessage("Welcome");          // Output: Welcome
logMessage("Welcome", "Alice"); // Output: Alice: Welcome

console.log(greetUser("John"));            // Output: Hello, John
console.log(greetUser("John", "Welcome")); // Output: Welcome, John

Optional parameters: user? is optional in logMessage. If no value is provided, it will be undefined.
Default parameters: greeting in greetUser has a default value of “Hello”.

4. Return Types and Type Inference

You can explicitly specify the return type of a function, but TypeScript is smart enough to infer it based on the returned value.

Example 4: Return Types

function multiply(a: number, b: number): number {
    return a * b;
}

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

const result = multiply(5, 3); // TypeScript infers result to be of type 'number'
log(result.toString()); // Output: 15

In the multiply function, the return type number is explicitly declared, but in many cases, TypeScript can infer the type. The log function has a return type of void because it doesn’t return a value.

5. Anonymous and Arrow Functions

TypeScript supports anonymous and arrow functions, just like JavaScript. You can use type annotations with these as well.

Example 5: Anonymous and Arrow Functions

const addNumbers = function (x: number, y: number): number {
    return x + y;
};

const multiplyNumbers = (x: number, y: number): number => x * y;

console.log(addNumbers(4, 5)); // Output: 9
console.log(multiplyNumbers(4, 5)); // Output: 

Arrow functions provide a more concise syntax, especially for shorter functions.

6. Function Overloading

TypeScript allows function overloading, where multiple function signatures are declared, and a single implementation handles all of them. This is useful when a function can behave differently based on the type or number of arguments.

Example 6: Function Overloading

function getLength(value: string): number;
function getLength(value: any[]): number;
function getLength(value: string | any[]): number {
    return value.length;
}

console.log(getLength("Hello"));   // Output: 5
console.log(getLength([1, 2, 3])); // Output: 3

In this example, the getLength function has two overloads: one for string and another for any[] (arrays). The implementation handles both cases.

7. Rest Parameters and Spread Syntax

TypeScript supports rest parameters, which allow you to pass an indefinite number of arguments into a function.

Example 7: Rest Parameters

function sumAll(...numbers: number[]): number {
    return numbers.reduce((sum, num) => sum + num, 0);
}

console.log(sumAll(1, 2, 3));    // Output: 6
console.log(sumAll(10, 20, 30)); // Output: 60

The …numbers syntax allows us to handle any number of arguments as an array within the function.

8. Function as a Type

In TypeScript, functions can also be treated as types. You can define a function type by specifying the types of the parameters and return value.

Example 8: Function as a Type

let mathOperation: (a: number, b: number) => number;

mathOperation = function (x: number, y: number): number {
    return x + y;
};

console.log(mathOperation(5, 10)); // Output: 15

In this example, mathOperation is a variable with a function type that takes two numbers as parameters and returns a number.

9. Generic Functions

Generic functions allow you to define functions that work with a variety of types, making your code more reusable and flexible.

Example 9: Generic Functions

function identity(arg: T): T {
    return arg;
}

console.log(identity(5));   // Output: 5
console.log(identity("Hi")); // Output: Hi

In this example, the identity function is a generic function where T represents any type passed to the function. When calling the function, you can specify the type explicitly, or TypeScript can infer it.

Conclusion

Functions in TypeScript are much more powerful and flexible than those in JavaScript due to TypeScript’s type system. With features like optional and default parameters, type inference, function overloading, and generics, you can write functions that are type-safe, clear, and reusable.

By understanding and utilizing these features, you can make your TypeScript code more robust and reduce the chances of runtime errors.

You may also like