Bitsy Explains
Home/Cursed JavaScript/Lesson 10

The return Trap

Why a function with return on its own line gives undefined, how JavaScript adds semicolons for you, and the lines that cause the opposite problem.

Lesson 10 of 103 min readVideo
Video thumbnail: The return Trap Watch the 90-second video
Explain like I'm five

JavaScript has a helper that adds semicolons, the little marks that end a line of code, wherever it thinks you forgot one. When it sees return alone at the end of a line, it decides you are finished and puts a semicolon right there. The object on the next line is never reached.

The curse

function getCat() {
  return
  {
    name: "Mia"
  };
}
console.log(getCat()); // prints: undefined

The function clearly returns an object with a name. Call it, and you get undefined, with no error and no warning.

What JavaScript does

The helper is called automatic semicolon insertion, or ASI. When a line ends and the code there could be read as a finished statement, JavaScript may add a semicolon for you.

return has a stricter rule. If a line break comes straight after return, JavaScript always ends the statement there. So the function above is read like this.

function getCat() {
  return;
  {
    name: "Mia"
  }
}
console.log(getCat()); // prints: undefined

A bare return; gives back undefined, and the lines below it never run. They don't even count as an object anymore. JavaScript reads the curly braces as a block of code with a label called name inside, which is still valid code. That is why there is no error to warn you.

The helper exists so tiny slips don't break whole programs, and most of the time it guesses right. After return, it guesses wrong.

How to break the curse

Put the opening curly brace on the same line as return.

function getCat() {
  return {
    name: "Mia"
  };
}
console.log(getCat()); // prints: { name: 'Mia' }

Now the line doesn't end after return, so JavaScript keeps reading and returns the object. The same goes for any long value. If you want to spread it over several lines, start it on the return line, for example with return ( and the rest below.

Bonus twist: when no semicolon is added

Sometimes the helper does the opposite. It only adds a semicolon when the next line can't continue the current one. A line that starts with [ or ( can continue the line above, so JavaScript glues the two lines together.

const a = 1
[1, 2].forEach((n) => console.log(n));

JavaScript reads this as one long line, const a = 1[1, 2].forEach(...). That looks up a property on the number 1, gets undefined, and crashes with "TypeError: Cannot read properties of undefined (reading 'forEach')".

Watch out for lines that start with [, ( or a template literal. This is why many people end every finished line of code with a semicolon. Tools like Prettier and ESLint can add them for you, or warn you when a line could be glued to the one above.

Rule of thumb

Keep the curly brace on the same line as return, and end each finished line of code with a semicolon.

Common mistake

Pressing Enter right after return to make a long value look tidy. Start the value on the same line, with return { or return (, so JavaScript knows more is coming.

Check yourself

A function has return alone on one line and an object on the next. What does calling it give you?