How To Write Functions In TypeScript
By: Wilson Ng
Tags: Programming, Web Development, React, JavaScript, Typescript

How To Write Functions In TypeScript

Beginner-friendly series to future proof your app

Hey There

The other parts of this series are below for your convenience. 🥳

Part 1 of future-proofing your app

Part 2 of future-proofing your app

We previously talked about prop-types and moving on to an introduction on TypeScript. Still, our TypeScript introduction was very brief and focused on the typed system of primitive data types. This post will move on to show how a function can first check the argument type before moving on to execute the function body. We will also look at some more complex types after exploring how TypeScript works with functions.

Photo by James Harrison on Unsplash

JavaScript Kinks

First, let’s take a look at the amount of extra code we need to handle type errors or unexpected behavior in JavaScript, and this is only when you actually have the experience and foresight to expect these sorts of things. JavaScript code can’t really expect the types of arguments passed in. A JavaScript function can take in any data type, but it is in the function body where we specify logic in handling unexpected data types.

JS code:

const findSum = (numOne, numTwo) => numOne + numTwo findSum(5, '6') -> outputs 56 * Notice the '6' is a string in this case and the integer 5 gets coerced into a string as well, resulting in a concatenated string value. Definitely not what we want in this case - and sure, we can write logic inside the function body like below. const findSum = (numOne, numTwo) => {

if(typeof numOne !== 'number' || typeof numTwo !== 'number'){

throw new Error('passed in args are not integers')

}

return numOne + numTwo

}

The second JavaScript code is definitely valid, and it works. However, there is a much simpler way to write this in TypeScript. The less code we write usually means the less code we have to read and maintain. Everybody ‘liked’ that!

TS code:

const findSum = (numOne:number, numTwo:number) => numOne + numTwo findSum(5, '6') * This will result in an error that looks like the line below. Error - Argument of type 'string' is not assignable to parameter of type 'number'.

We can take advantage of type annotations with the same syntax when declaring variables — <variable/parameter: desired data-type>. The type system within TypeScript will handle the logic of unexpected data type and throw the error without writing the extra code. Note that a type of ‘any’ will be inferred if no type is provided next to the parameter.

Functions, Parameters, Arguments…Battlestar Galactica?

Remember how JavaScript likes to silently fail most of the time and tend to throw undefined whenever there’s a small bug somewhere? There are times when it is due to missing arguments, and it won’t fly the same way with TypeScript. Let’s see how the TypeScript error will look with the code below.

JS code:

const findSum = function (numOne, numTwo) => {

console.log(numOne, numTwo) ------> // 6, undefined

return numOne + numTwo

} console.log(findSum(6)) ------------> // NaN TS code:

const findSum = (numOne:number, numTwo:number) => numOne + numTwo findSum(5) Error - Expected 2 arguments, but got 1.

- An argument for 'numTwo' was not provided.

The errors are beneficial, and there’s definitely less flexibility unless specified explicitly with optional parameters in TypeScript.

JS code:

const greeting = function (name) => {

return "hello " + (name || 'stranger')

} console.log(greeting()) // hello stranger TS code:

const greeting = (name?:string) => { <-- notice the question mark?

return `hello ${name || 'stranger'}`

} The question mark after the parameter allows developers to make a given parameter optional.

Assigning default values to parameters looks the same as JavaScript.

TS code:

const greeting = (name = 'stranger') => {

return `hello ${name}`

} console.log(greeting()) // outputs 'hello stranger'

TypeScript will perform type inference here based on the default value’s type. So, in this case, the default value is a type ‘string’ — receiving any other data type will result in an error. Since we are on the topic of type inference, it’s also worth mentioning that TypeScript infers the return type by looking at the value after a return statement. In the code above, the return type of the greeting function will be a ‘string.’ If for some reason, you have to store the returned value into another variable that specifies a data type — it will result in an error being thrown. Now let’s also look at explicitly stating return types.

TS code:

const myAge = (age = '18'): string => {

return Number(18)

// returning any other data type will cause an error

} // explicit annotation after closing parenthesis to state return type // Type 'number' is not assignable to type 'string'.

If a function returns undefined because of a missing argument or forgetting to return a value, TypeScript will produce a helpful error because undefined is also a data type. Super neat!

Conclusion

Thank you for joining me on my journey to learn TypeScript. I personally see the usefulness of TypeScript to safeguard our code. It certainly helps with readability and takes away the guesswork of what a function is supposed to receive and return when working in a big engineering environment.

Hopefully, I was able to be clear about my thoughts, and we all learned a lot from this article. Now we know how to read and write type annotations on parameters, give default values just like in JavaScript, and understanding how to determine or explicitly state a return type for a function. This is probably more than half the battle already because functions are treated as first-class citizens able to be passed around to other functions.

The next post on TypeScript will cover applying types to more complex data structures like arrays and objects. Always be learning, am I right?! See you all on the next one.

References