Return Values in Typescript

Written 2019-11-01

If you are programming web-apps, front ends or even just small home pages like this, chances are you are using Javascript or Typescript. While Javascript is a staple for many programmers, it has some tricky corner cases that you have to watch out for. In this post I will show you one of them, involving arrow functions. Consider the following, seemingly innocuous code Typescript fragment:

async function getTen() {
    return(10);
}

async function addNumber(n: number) {
    getTen().then(x => { x + n })
}

addNumber(10).then(x => console.log(x))

At the first glance many programmers will probably expect this code to print 10 on the console. You can try this in your browser here. What you will find is that, in fact, the previous example code prints undefined. So what gives?

What you may have stumbled on here is the difference between statements and expressions.  x + n is an expression while { x + n } is a statement. So the "value" of x + n is just that, while the value of {x + n} is undefined.

So how can the code above be fixed? There are two variants:

async function addNumberExpr(n: number) {
    getTen().then(x => x + n )
}

async function addNumberReturnStatement(n: number) {
    getTen().then(x => {return (x + n)} )
}
In the first variant we convert the statement in the function body to an expression and in the second one we use a return statement. Either of these will work just fine. In case you ever see unexpected behaviour in code using arrow functions, it may be worth re-checking if all your arrow functions actually return (expression) values.

In Typescript, such problems can fortunately be side-stepped completely, by using explicit type signatures. If the function from the first example looked like this:

async function addNumber(n: number): Promise<number>{
    getTen().then(x => { x + n })
}
This code would never pass the compiler and you would see an error message similar to this:
A function whose declared type is neither 'void' nor 'any' must
return a value.

This example highlights once more how static typing can prevent you from shooting yourself in the foot in the first place. To avoid unexpected behaviour from arrow functions, you can either diligently check your code to never use curly braces or you annotate all functions with the type you expect them to return, if are using Typescript.