2019-02-11 07:52:57 8 Comments
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?
Related Questions
Sponsored Content
25 Answered Questions
[SOLVED] How does JavaScript .prototype work?
- 2009-02-21 12:31:18
- John Leidegren
- 479942 View
- 1918 Score
- 25 Answer
- Tags: javascript dynamic-languages prototype-oriented
12 Answered Questions
[SOLVED] Why does JavaScript only work after opening developer tools in IE once?
- 2011-10-12 15:44:37
- James Bruce
- 144236 View
- 617 Score
- 12 Answer
- Tags: javascript internet-explorer internet-explorer-9
86 Answered Questions
[SOLVED] How do JavaScript closures work?
- 2008-09-21 14:12:07
- e-satis
- 1314188 View
- 7653 Score
- 86 Answer
- Tags: javascript function variables scope closures
5 Answered Questions
[SOLVED] What is TypeScript and why would I use it in place of JavaScript?
- 2012-10-02 16:37:58
- Mohammed Thabet
- 425444 View
- 1446 Score
- 5 Answer
- Tags: javascript typescript
14 Answered Questions
[SOLVED] .trim() in JavaScript not working in IE
- 2010-02-22 00:37:53
- Jin Yong
- 215571 View
- 455 Score
- 14 Answer
- Tags: javascript internet-explorer trim
11 Answered Questions
[SOLVED] Why don't self-closing script tags work?
- 2008-09-16 06:52:38
- dimarzionist
- 112772 View
- 1211 Score
- 11 Answer
- Tags: javascript html internet-explorer xhtml
4 Answered Questions
[SOLVED] Why aren't ◎ܫ◎ and ☺ valid JavaScript variable names?
- 2011-09-17 00:05:07
- Peter Olson
- 46811 View
- 707 Score
- 4 Answer
- Tags: javascript naming-conventions invalid-characters
16 Answered Questions
[SOLVED] Why would a JavaScript variable start with a dollar sign?
- 2008-10-15 18:26:34
- Ken
- 205989 View
- 945 Score
- 16 Answer
- Tags: javascript naming-conventions
7 Answered Questions
[SOLVED] Why doesn't indexOf work on an array IE8?
- 2010-09-02 16:33:54
- nLL
- 119784 View
- 291 Score
- 7 Answer
- Tags: javascript internet-explorer internet-explorer-8 indexof
2 Answered Questions
[SOLVED] Possible to throw new errors from Ajax success event?
- 2012-02-29 16:15:41
- kaspnord
- 2126 View
- 3 Score
- 2 Answer
- Tags: javascript jquery ajax
5 comments
@Patrick Hollweck 2019-02-11 08:06:27
Your problem is that an assignment expects an expression but you give it a statement
The Syntax for initializing/assigning a variable is:
but you use
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
Examples for Expressions:
Literals
x
is assigned the value "5"A function call
myFunc()
produces a value that is assigned to xThe 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
This loop performs the action of executing the loop body 10 times
Throwing an 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 youdo (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 thatThis works because the right side is now a function and a function is an expression which produces a value.
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:
@TheHansinator 2019-02-11 13:54:51
As an aside, C# 6 allowed "throw" to be an expression precisely to enable scenarios like this - the return type of the expression was inferred from context. I imagine that something like this would be even easier to conceptually add to JavaScript, since it doesn't check return types at compile time.
@jcaron 2019-02-11 17:37:26
Nitpicking, it's the
||
operator which is expecting two expressions here, not the assignment.@The Vee 2019-02-11 18:13:53
It's also an expression (of type
void
) in C++.@6502 2019-02-13 09:16:23
Like others have said the problem is that
throw
is a statement and not an expression.There is however really no need for this dichotomy. There are languages where everything is an expression (no statements) and they're not "inferior" because of this; it simplifies both syntax and semantic (e.g. you don't need separate
if
statements and the ternary operator?:
).Actually this is just one of the many reasons for which Javascript (the language) kind of sucks, despite Javascript (the runtime environment) being amazing.
A simple work-around (that can be used also in other languages with a similar limitation like Python) is:
then you can simply write
There is some complexity in having
throw
as an expression for statically typed languages: what should be the static type ofx() ? y() : throw(z)
?; for example C++ has a very special rule for handling a throw expression in the ternary operator (the type is taken from the other branch, even if formallythrow x
is considered an expression of typevoid
).@Cristik 2019-02-14 00:30:42
Just a note that your work-around was already provided in stackoverflow.com/a/54626116/1974224.
@Beefster 2019-02-12 00:38:02
As other answers have stated, it is because
throw
is a statement, which can't be used in contexts which expect expressions, such as on the right side of a||
. As stated by others, you can get around that by wrapping the exception in a function and immediately calling it, but I'm going to make the case that doing so is a bad idea because it makes your intent less clear. Three extra lines of code is not a big deal for making the intent of your code very clear and explicit. I personally think thatthrow
being statement-only is a good thing because it encourages writing more straightforward code that is less likely to cause other developers to scratch their heads when encountering your code.The
||
defaulting idiom is useful when you want to provide default or alternative values forundefined
,null
, and other falsy values, but I think it loses a lot of its clarity when used in a branching sense. By "branching sense", I mean that if your intent is to do something if a condition holds (the doing something in this case being throwing an exception), thencondition || do_something()
is really not a clear way to express that intent even though it is functionally identical toif (!condition) {do_something()}
. Short-circuit evaluation isn't immediately obvious to every developer and||
defaulting is only understood because it's a commonly-used idiom in Javascript.My general rule of thumb is that if a function has side effects (and yes, exceptions count as side effects, especially since they're basically non-local goto statements), you should use an if statement for its condition rather than
||
or&&
. You're not golfing.Bottom line: which is going to cause less confusion?
or
It's usually worth it to sacrifice terseness for clarity.
@ttulka 2019-02-12 06:55:05
An exception is a side effect indeed, but - if not used for flow control - it's just an exception (can lead to a system crash, and that's fine), not a flow control. Using
if
statement attracts the eye of the reader and mentally makes from an exception to a flow control, which is wrong, because exceptions should not be used in that manner. Written as I proposed makes an exception to an assertion, which is better (could be easily ignored by the reader). But maybe the best would be not to use such checks at all and let the runtime itself deal with wrong parameters...@Beefster 2019-02-12 07:57:39
@ttulka If your intent is to have an assertion, you would be far better off having an
assert
function defined somewhere that wraps this intention. Something likefunction assert(value, message) { if (value) {return value} else {throw new Error(message || 'assertion failed')}}
will do just fine. Or you could use an assertion library. You can even remove the assertion for release builds withassert = () => {}
@Beefster 2019-02-12 08:04:12
And even if an exception isn't used for flow control, it still effectively acts as a
goto somewhere
withcatch
statements effectively beingcomefrom
s. Now this can be reasonable in many cases, especially when in JS they're used more likepanic
s and caught at the top of some sort of dispatcher, but exceptions can often lead to surprises when not caught at the API boundaries.@ttulka 2019-02-12 08:46:18
I don't think it is same. If I don't do the explicit check, an exception will be thrown anyway, I just want to "customize" it. The idea with
assert
is okay, but it's not different from proposals in other answers. My motivation is to use language constructs directly without any boilerplate code.@Beefster 2019-02-12 08:51:56
An if statement isn't boilerplate. You don't even have to include the braces if all you're doing is throwing an exception. Then you can keep it on the same line.
@ttulka 2019-02-12 08:54:28
if
statement isn't boilerplate in general, but it is in this case, becauseun || throw new Error('fail')
is much simpler. But this is a taste thing, so no need to discuss it on here.@Nina Scholz 2019-02-11 07:56:40
You could move the throwing of the exception into a function, because
throw
is a statement of control flow, and not an expression:@Dan Robertson 2019-02-11 10:09:09
Why not just have
const throwf = function(err) { throw err }
and then it can be used anywhere.@T.J. Crowder 2019-02-12 10:14:00
Of note:
throw
expressions are at Stage 2 of the process for being added to the language. :-) Until/unless that happens, a function wrapper like the (updated) one above is the simple workaround.@CertainPerformance 2019-02-11 07:57:03
throw
is a statement only; it may not exist in a position where an expression is required. For similar reasons, you can't put anif
statement there, for exampleis 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 tothrow
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:But that's pretty weird. I'd prefer the explicit
if
-throw
.@usr 2019-02-11 19:47:46
It's worth pointing out that
throw
could have been made an expression. Maybe a future language version will enable this pattern.@Ben Blank 2019-02-11 20:08:32
In fact, there's already a stage 2 proposal to add throw expressions to the language. Alas, it'll still be a while before it's available for use.
@KRyan 2019-02-12 04:57:25
Our project—in Typescript, so the pattern is perhaps more transparent—we have a core utility module that includes
export function crash(message: string): never { throw new Error(message); }
and all throws are done via this function. Useful because now it’s an expression (and thenever
return annotation indicates it won’t return because it throws), and because we can put a breakpoint in it (we have a high-level catch block to produce a custom error message rather than it merely being sent to the console, but this can prevent the debugger from breaking on the throw).