TypeScript function

·

4 min read

Introduction

This article will teach us how to write types that describe functions. As we know functions are essential building blocks of an application. We have used functions in various forms like callback functions, class methods, and functions from other modules.

Functions are also values, and in TypeScript, every value has a type. So let's learn how to annotate functions with types.

Function Type

We need to specify two things when annotating a function with types. First, what type of parameters it takes, and second what type of value it returns.

// Traditional function with types
// take two parameters of number type and return a number
function sum(a: number, b: number): number{
    return a + b;
}    
// Arrow function with types
const sum = (a:number, b:number) : number => a + b;

By default, the parameters type and return type is any.

// this function is equivalent the below function.
function sum (a,b){
    return a + b;
}
// function with default types
function sum(a:any, b:any):any{
    return a + b;
}

If you don't specify a return type of a function, TypeScript will infer it based on the return value.

// here return type is string
function add (a: string, b: string){
    return a+b; // return string
}
// here return type is number
function add (a: number, b: number){
    return a+b;// return number
}

Callback function type

We can pass functions as arguments in other functions where they are invoked. Let's see how to annotate them with types.

// cb is a callback function of type "(a:string) => void"
function sayHello(cb : (a:string) => void ){
    cb("Hello World!");
}

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

sayHello(pringMsg);

In the above code, (a:string) => void represents a function expression type. It represents a function that takes a string parameter and returns nothing.

Function type expression can make our function verbose, we can use type aliases to make code less verbose.

type CbFunctionType = (a:string) => void;

function sayHello(cb : CbFunctionType ){
    cb("Hello World!");
}

Constructor function type

A function that is used to create objects is called a constructor function, and it is called with new keyword.

function User(name:string){
    this.name = name;
};

type UserConstructorType = {
    new (a: string) : SomeObj; 
}

function createUser(constrFun : UserConstructorType){
    return new constrFun("Jhon");
}

Optional Parameters

Functions in JavaScript take a variable number of parameters.

In TypeScript, we use "?" at the end of a parameter name to make it optional.

function foo (num: number, fixed?: number  ){
  if(fixed !== undefined){
      console.log(num.toFixed(fixed)); // 12.224 
  }else{
     console.log(num.toFixed()); // 12
  }
}
foo(12.2243, 3); 
foo(12.33);

Note: Optional parameters must be specified after the required parameters, otherwise typescript compiler throws an error.

// Error: A required parameter cannot follow an optional parameter.
function foo(a?:string, b:string){
    //...
}

Rest Parameters

Rest parameters help us to define a function that can take an indefinite number of parameters. The rest parameter is preceded by three dots (...).

By default, the rest parameters are of any[] type.

Note: The rest parameter must be the last parameter.

function add(val: number, ...rest: number[]) {
  return rest.map((num) => val  + num);
}

// 'arr' gets value [11, 12, 13, 14]
const arr = add(10, 1, 2, 3, 4);
// 'arr' gets value [101, 102, 103, 104]
const arr2 = add(100, 1,2,3,4);

Parameter Destructuring

Parameter destructuring makes it easy to access object properties in the function body without using the dot operator or square [] bracket.

function sum({ x, y, z } : {x:number, y: number, z:number}) {
  console.log(x + y + z);
}
const obj = { x: 10, y: 3, z: 9 }
sum(obj);

In the above code, we can see that object type makes our code verbose. What if an object has many properties?

Type Alesis can help us here. We can define object types separately.

type ObjType = {
 x:number;
 y:number;
 z:number
}

function sum({ x, y, z } : ObjType) {
  //...
}
const obj = { x: 10, y: 3, z: 9 }
sum(obj);

What if the object is nested? How can we destructure nested objects?

Let's see an example of destructuring of a nested object.

type User = {
    id: number;
    name: string;
    address: {
        country: string;
        state: string;
        city: string;
    };
}

function createUser(
    {id, name, address : { country, state, city}} : User ) {

    //...
}

Conclusion

In this article, we learned how to use type with functions. How to use rest parameters, parameter restructuring, and optional parameters.

Hope you find it useful. Feel free to write any suggestions in the comments section.