Book Your Free Call

The Complete Guide On TypeScript Functions

Tim Mouskhelichvili
Tim Mouskhelichvili
7 minutes to read

Functions are part of the basic fundamental concepts of programming that every developer needs to understand. A function is a reusable piece of code that performs a specific task. Functions are the fundamental building blocks of any web application. In TypeScript, just like in JavaScript, functions can be declared in multiple ways.

The easiest way to declare a function in TypeScript is to use the function keyword:

typescriptfunction getABC(): string {
    return 'abc';
}

This article will be an in-depth guide on TypeScript functions. We will explore such concepts as different function syntaxes, different parameters, destructuring, return objects, and many more themes.

Let’s get to it 😎.

typescript function

Syntax (with regular functions)

The easiest and oldest way of declaring functions in TypeScript is to use regular functions.

You can declare a regular function, either by creating a function declaration or by using function expressions.

A function declaration is when you create a named function.

typescriptfunction getName(): string {
    return 'Tim';
}

A function expression is when you create an anonymous function (a function without a name).

typescriptconst getName = function (): string {
    return 'Tim';
};

However, in most cases nowadays, I recommend using the arrow functions.

Syntax (with arrow functions)

Arrow functions are a new feature introduced in ES6. They are also sometimes called "fat arrow functions" or "lambda functions".

It is a new way of writing functions and the one that developers are using the most nowadays.

typescriptconst getName = (): string => {
    return 'Tim';
};

As you can see, an arrow function is always anonymous.

Differences between regular and arrow functions

The most important difference between regular functions and arrow functions is the this keyword behavior.

Inside a regular function, the this value is dynamic and depends on how it is invoked.

Inside an arrow function, the this value always equals to the this value of its closest non-arrow parent function.

javascriptconst person = { 
    getNameArrow: () => { 
        console.log(this.name);
    }, 
    getNameRegular() { 
        console.log(this.name);
    },
    name: 'Tim'
};

// Outputs: 'Tim'
person.getNameArrow();

// Outputs: undefined
person.getNameRegular();

Here is a table with all of the other differences between an arrow function vs a regular function:

Arrow functionRegular function
Arguments bindingDoes not have arguments binding but has access to the arguments object of its closest non-arrow parent function.Regular functions have arguments binding.
ConstructorsYou cannot construct an object with an arrow function. You cannot call an arrow function with the new keyword.You can construct an object with a regular function. You can call a regular function with the new keyword.
Implicit returnIf an arrow function contains one expression and you omit curly braces the result of the function will be implicitly returned.Regular functions do not have an implicit return.

When to use arrow function vs regular function?

Only use a regular function:

  • In the global scope.
  • Inside an object prototype.
  • Inside callbacks with dynamic context.
  • Inside an object literal.
  • If you want to invoke the function as a constructor.

Everywhere else, use an arrow function.

Invocation

To invoke a function use the () operator.

typescriptconst getName = (): string => {
    return 'Tim';
};

// Outputs: 'Tim'
getName();

A function can also be self invoked, meaning that the function is executed automatically and do not need a function call.

typescriptconst name = ((): string => {
    return 'Tim';
})();

Function return

A function can either return nothing (void) or a return value. A function can have a return type of primitive, an interface or type, or an object.

To return a value from a function you need to use the return keyword.

typescriptconst getName = (): string => {
    let name = '';

    name += 'Tim';
    name += ' Mousk';

    return name;
};

With arrow functions, you can return implicitly, but only if you respect those two conditions:

  • Only one expression is present in the function.
  • You omit the curly braces.

Here is an example of an implicit return:

typescriptconst getAge = (year: number): number => new Date().getFullYear() - year;

Function types

In TypeScript, you have the ability to type your functions. You can type parameters, as well as specify a return type.

Here is an example of a typed function:

typescriptconst getName = (firstName: string, lastName: string): string => {
    return `${firstName} ${lastName}`;
};

By typing your functions, you make your code more reliable, maintainable, and easy to debug. Also, typing your functions increase the performance of your team. Honestly, there are only advantages to doing that.

Optional parameters

In TypeScript, you can allow a named parameter to be optional. You can make a parameter optional by using the ? operator.

Here is an example of a function with an optional parameter:

typescriptconst getName = (firstName?: string): string => {
    return firstName || 'No name specified';
};

// Outputs: 'No name specified'
console.log(getName());

// Outputs: 'Tim'
console.log(getName('Tim'));

Optional parameters must always be declared after the required parameters.

If you do not pass a value to an optional parameter, its value will be undefined.

Default parameters

In TypeScript, you can allow a named parameter to have a default value if no value or undefined is passed to it.

Here is how you define a default parameter:

typescriptconst getName = (firstName = 'Guest'): string => {
    return firstName;
};

// Outputs: 'Guest'
console.log(getName());

Default parameters must always be declared after the required parameters.

Rest parameters

In TypeScript, when you don't know exactly how many parameters a function will receive, you need to use the rest parameter feature.

A rest parameter is defined with the ... operator.

Here is an example of a function with a rest parameter:

typescriptconst getNames = (...names: string[]): string => {
    return names.join(', ');
};

// Outputs: 'Tim, John, Mark'
console.log(getNames('Tim', 'John', 'Mark'));

A rest parameter acts like an array.

You can pass zero or more arguments to a rest parameter.

The rest parameter must always be the last parameter of a TypeScript function signature.

Parameter destructuring

Parameter destructuring is a nice way to unpack an object provided as a parameter into local variables that can be used inside of the body of the function.

Here is an example of a function that uses parameter destructuring:

typescriptinterface IPerson {
    firstName: string;
    lastName: string;
    age: string;
}

const getName = ({ firstName, lastName }: IPerson): string => {
    return `${firstName} ${lastName}`;
};

You can combine parameter destructuring with default and optional parameters.

typescriptinterface IPerson {
    firstName: string;
    lastName?: string;
    age: string;
}

const getName = ({ firstName, lastName = 'No last name' }: IPerson): string => {
    return `${firstName} ${lastName}`;
};

Recursive functions

In the most simple terms, a recursive function is a function that calls itself.

Using a recursive algorithm, you can easily implement quicksort, binary search, the fibonacci sequence, and many more.

Here is an example implementation of the fibonacci sequence using recursion in TypeScript:

typescriptconst fibonacci = (num: number): number => {
    if (num < 2) {
        return num;
    }
    
    return fibonacci(num - 1) + fibonacci(num - 2);
};

Recursive functions are very frequently the subject of coding algorithmic and technical interviews.

Function utility types

TypeScript provides a lot of utility types to help developers work with a function type.

Here is a list of those utility types:

  • Parameters<Type> - It creates a tuple from the types of the parameters of a function type.
  • ReturnType<Type> - It constructs a type from the return type of the function type.
  • ThisParameterType<Type> - It extracts the this parameter from a function type.
  • OmitThisParameter<Type> - It removes the this parameter from a function type.
  • ConstructorParameters<Type> - It creates a tuple or array from the types of a constructor function type.

Overload signatures (function overloads)

Sometimes a developer might want to create functions of the same name but with different signatures. For example, he might want to use different return types or a different number of parameters with the same function. This situation is called function overloading.

Luckily for us, TypeScript supports function overloads. To declare a function overload we must use overload signatures.

Here is an example of TypeScript function overloading:

typescriptfunction getName(firstName: string): string;
function getName(firstName: string, lastName: string): string;

function getName(firstName: string, lastName?: string): string {
    if (lastName) {
        return `${firstName} ${lastName}`;
    } 
    return firstName;  
}

// Outputs: 'Tim'
console.log(getName('Tim'));

// Outputs: 'Tim Mousk'
console.log(getName('Tim' , 'Mousk'));

In this example, we wrote two overload signatures and also the function implementation.

Construct signatures

In JavaScript, you can invoke a function using the new keyword (also called a constructor function). In TypeScript, you can define those kinds of functions using the construct signature.

Here is an example of this:

typescripttype MyConstructor = {
    new (s: string): string;
};

function getName(fn: MyConstructor): string {
    return new fn('Tim');
}

Generic Functions

Generics are a great way to make a function more flexible and reusable. Generics open a function and do not restrict it to one particular data type. TypeScript has great support for generic functions.

Here is an example of a generic function in TypeScript:

typescriptconst joinArray = <T>(items: T[]): string => {
    return items.join(',');
};

const arr1 = joinArray<number>([ 1, 4, 2 ]);
const arr2 = joinArray<string>([ 'Tim', 'Mousk' ]);

Final thoughts

As developers, functions are part of our daily lives.

We use them every day.

That's why it is important to not only understand them well but also to know some of their more advanced concepts. Understanding those will help you become a better developer. Also, if you are a student, knowing those concepts will help you land your first job.

typescript function

I have written many more tutorials on TypeScript if you are interested in learning more about it.

If you have any questions please ask them in the comments section. It will be my pleasure to answer them to the best of my abilities.

Thank you for reading through this article.

Please share it with your fellow TypeScript developers and others.

Comments (0)