Func-tastic TypeScript

Func-tastic TypeScript

The Magic of Functions

Cover photo credit: Ceos3c

Introduction

You are reading this article because of a function (a method to be more precise). Functions make up an "underrated" most of our functional technology, and this is no different from Typescript(TS). You might understand the logic and syntax behind functions in Javascript(JS), and if you are curious to know how it works in TS, I've got you covered in this week's article.

Function Inference

Function with single parameters

Check out the function below:

function addTwo(num) {
  return num + 2;
}

addTwo(46);

The code above looks all good, and it is. However, if we just hover around it to check its data type, it is inferred to as Any(Discussed in the previous article of this series). We know that's bad for our team because Any escape's type and static checking and attract messy bugs.

We can fix this by simply mandating the parameter to be a number. Here:

// Specify data type
function addTwo(num: number){
    return num + 2
}

addTwo("46"); // Notice this error
  • In the code above, I threw in an error that would trigger static checking. The point you should note is that if I did not specify the data type of the number, then TS would not have noticed that error. Functions in TS have this behavior and it is up to you to specify that you don't want the inferred Any data type.

Function with multiple parameters

Let's have a function with multiple parameters:

function signUpUsers(name, age, isRegistered) {
  console.log(name, age, isRegistered);
}

signUpUsers(22, "thirty-three", 3);

In the code above, static checking on all parameters returns each parameter as an Any data type. The signUpUsers function receives inputs that are not correct in this context, and TS can't warn us with static checking. We can simply add a data type to each parameter to avoid this.

function signUpUsers(name: string, age: number, isRegistered: boolean) {
  console.log(name, age, isRegistered);
}

signUpUsers(22, "thirty-three", 2); // error activates static checking
  • Static checking is now activated above, and we start getting those error warnings

Arrow functions

Sometimes it might be necessary to use arrow functions and use default parameters, while prompting a user for input for other parameters, something like this:

let okayLogin = (name: string, email: string, isRegistered: Boolean) => {
  // Some code
};

okayLogin("kelvin", "kelvin@kelvin.com");
  • In the last line above, the isRegistered parameter is not satisfied and TS static checking throws up an error.

Assuming we wanted to set isRegistered to a default boolean, and also wanted TS to ignore static checking for that, we could set a default value for it.

let okayLogin = (name: string, email: string, isRegistered: Boolean = true) => {
  // Some code
};

okayLogin("kelvin", "kelvin@kelvin.com");
  • The boolean is auto set to true except a different value is given.

Return statements

Single return statements

For most of the function examples, we have only looked at making the input match the data type of the parameters. But can the return statements also display type errors? Let's find out. In the snippet below:

function addNumbers(num: number) {
  return "hello";
}
  • Since the parameter is a number, I am also assuming that the output should be a number, but TS doesn't give any static check warnings.

If this was a team with hundreds of thousands of lines of code, this would be an issue. To solve this, we have to specify the output:

function addNumbers(num: number): number {
  // specify output
  return "hello"; // error
}
  • After specifying what should be returned, TS static checking comes into play.

Multiple return statements

What if we had a function that could return different data types based on conditions?

function updateStatus(val: number) {
  if (val > 5) {
    return true;
  } else {
    return "status 404 not found";
  }
}
  • The output of the function above is open to any data type which defeats the purpose of Typescript.

Specifying a return statement for one data type will enforce static checking errors, so we have to specify multiple return statements. Like below:

function updateStatus(val: number): boolean | string {
  if (val > 5) {
    return true;
  } else {
    return "status 404 not found";
  }
}

It is also simple to transform the code above to an arrow function:

let updateStatus = (val: number): boolean | string => {
  if (val > 5) {
    return true;
  } else {
    return "status 404 not found";
  }
};

Void and Never functions

Sometimes we might want a function that to return nothing, to do this, we specify the function to return a void type or a never type.

with void

function handleError(error: string): void {
  throw new Error(error);
}
  • The void type does static checking, if any attempt to mutate the function to add a return statement to it.

with never

Meanwhile, never more suited to throwing errors:

function handleError(error: string): never {
  throw new Error(error);
}

Difference between void and never

In TypeScript, void and never are both used to represent the absence of a value, but they have different meanings.

void is a type that represents the absence of a value. It is typically used as the return type of functions that do not return a value, or when a variable is not assigned any value. For example, the following function returns void:

function logMessage(message: string): void {
  console.log(message);
}
  • In this case, the function logMessage does not return a value, so its return type is void.

On the other hand, never is a type that represents a value that never occurs. It is typically used to indicate that a function is meant to never return a value or that a variable cannot have a value. For example, the following function returns never:

function throwError(error: string): never {
  throw new Error(error);
}
  • In this case, the function throwError throws an error and never returns a value, so its return type is never.

It is the intent that matters, void should be used to indicate that a function does not return a value, while never indicates that a function should never have a value.

Conclusion

Functions have proven themselves to be the unsung heroes of our coding escapades, and in Typescript, they take on a whole new level of charm. By harnessing the power of static type checking, we can sprinkle our functions with enchanting data types and wave goodbye to pesky bugs.

From inferring the right types for single and multiple parameters to crafting arrow functions with default values that tickle our coding taste buds, Typescript has us covered. It's like a magical spell that keeps our code in check, ensuring we play by the rules and catch errors before they have a chance to misbehave.

But it doesn't stop there! Our trusty functions can even entertain us with their return statements. We've learned how to coax them into revealing their true type identities, whether it's a single type or a delightful union of possibilities. We've witnessed the power of void, where functions gracefully bow out without returning a value, and never, the enigmatic type that assures us a value will never, ever emerge.

So, as we bid farewell to this journey through Typescript functions, let's remember to embrace their quirks and unleash their full potential. With static checking as our guide, we can create code that dances with joy, bringing laughter and stability to our programming endeavors. So go forth, fellow developers, and let your functions shine with all their magical might!