Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't logical OR work with error throwing in JavaScript?

Tags:

javascript

This is a pretty common and useful practice:

// default via value var un = undefined var v1 = un || 1  // default via a function call var myval = () => 1 var v2 = un || myval() 

But it doesn't work (SyntaxError) when throwing an error:

var v3 = un || throw new Error('un is not set!') 

Is there a way how to achieve the same effect in a similarly elegant way? This is IMHO a lot of boilerplate code:

if (!un) {     throw new Error('un is not set!') } var v3 = un 

Or is there any theoretical obstruction, why this is not, and never will be, possible?

like image 207
ttulka Avatar asked Feb 11 '19 07:02

ttulka


People also ask

What happens when you throw an error in JavaScript?

The throw statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate.

Should you throw errors in JavaScript?

It's best to avoid throwing errors from inside a Promise, because they may not always be caught, depending on how the code that called them is structured. However it's good practice to return an error when rejecting a Promise, and you can return Error custom types just like any other Error.

Why throw new error is not working?

The error probably is thrown, but the function that throws the error is being called from an event handler so the error ends up in the system event handling code where it's either caught there (not caught in your code) or it simply gets ignored.


2 Answers

Your problem is that an assignment expects an expression but you give it a statement

The Syntax for initializing/assigning a variable is:

var|let|const <variableName> = <expression> 

but you use

var|let|const <variableName> = <statement> 

which is invalid Syntax.

Expressions

An expression is something that produces a value.

What is a "value"?

A value is anything that is a type in Javascript

  • Numbers
  • Strings
  • Booleans
  • Objects
  • Arrays
  • Symbols

Examples for Expressions:

Literals

var x = 5; 

x is assigned the value "5"

A function call

var x = myFunc(); 

myFunc() produces a value that is assigned to x

The produced value of a function is its return value - A function always returns, and if it doesn't explicitly, it returns undefined.

Functions have the added benefit of being able to contain statements in their body - Which will be the solution to your question - But more on that later.

Statements

A statement is something that performs an action. For Example:

A loop

for (var i = 0; i < 10; i++) { /* loop body */ } 

This loop performs the action of executing the loop body 10 times

Throwing an error

throw new Error() 

Unwinds the stack and stops the execution of the current frame

So why can't we mix both?

When you want to assign to a variable, you want an expression because you want the variable to have a value.

If you think about it, it should be clear that it will never work with a statement. Giving a variable an "action" is nonsense. What is that even supposed to mean?

Therefore you cannot use the throw statement since it does not produce a value.

You can only have one or the other. Either you are (expression) something or you do (statement) something.

A fix

You can convert any statement into an expression by wrapping it in a function, I suggest using an IIFE (Immediately invoked function expression) - basically a function that invokes itself - to do just that

var x = 5 || (() => throw new Error())() 

This works because the right side is now a function and a function is an expression which produces a value, The value is undefined in this case, but since we stop executing it doesnt matter anyways.

Future Possibilities

Technically there is nothing that prevents this from working.

Many languages (c++, ...) actually already treat throw as an expression. Some (kotlin, ...) even leave out statements completely and treat everything as an expression.

Others (c#, php, ...) provide workarounds like the ?? null-concealing or ?. elvis operator to solve this very use case.

Maybe in the future we get one of those features into the ecmascript standard (there is even an open proposal to include this) until then your best bet is to use a function like:

function assertPresent(value, message) {   if(!value) {     throw new Error(message);   } else {     return value;   } } 
like image 25
Patrick Hollweck Avatar answered Oct 18 '22 08:10

Patrick Hollweck


throw is a statement only; it may not exist in a position where an expression is required. For similar reasons, you can't put an if statement there, for example

var something = false || if (cond) { /* something */ } 

is invalid syntax as well.

Only expressions (things that evaluate to a value) are permitted to be assigned to variables. If you want to throw, you have to throw as a statement, which means you can't put it on the right-hand side of an assignment.

I suppose one way would be to use an IIFE on the right-hand side of the ||, allowing you to use a statement on the first line of that function:

var un = undefined  var v2 = un || (() => { throw new Error('nope') })();

But that's pretty weird. I'd prefer the explicit if - throw.

like image 126
CertainPerformance Avatar answered Oct 18 '22 08:10

CertainPerformance