Why is `const x = a ?? b || c;` a SyntaxError in JavaScript, and how do you express either intended grouping?
answer
- the parser refuses, it is not a runtime issue
- two different fallback tests in one expression
- the committee made you say which you meant
- parentheses restore both readings
- chaining with itself is still allowed
basics
~20 sJavaScript forbids combining ?? with || or && in one expression without parentheses, so the parser rejects it. Write (a ?? b) || c or a ?? (b || c) to state the grouping explicitly; the two mean different things.
solid answer
~50 sThe grammar deliberately makes an unparenthesised mix of `??` with `||` or `&&` a SyntaxError. The two operators have different fallback rules — falsy versus nullish — so any implicit precedence would silently pick a meaning the reader might not intend, and the committee chose to force the author to be explicit instead. You fix it with parentheses: `(a ?? b) || c` first substitutes a nullish `a`, then applies a falsy check to the result, while `a ?? (b || c)` keeps `a` whenever it is non-nullish and otherwise picks between `b` and `c` by truthiness. Note the restriction is only against `&&` and `||`; `??` chains freely with itself (`a ?? b ?? c`), binds looser than arithmetic and comparison, and binds tighter than the conditional operator, so `a ?? b ? c : d` parses as `(a ?? b) ? c : d`.
code
javascript · 9 linesconst v = 0;
// const bad = v ?? 10 || 'fallback'; // SyntaxError: cannot mix ?? with ||
console.log((v ?? 10) || 'fallback'); // 'fallback' — 0 survives ??, then fails ||
console.log(v ?? (10 || 'fallback')); // 0 — v is not nullish, right side unevaluated
console.log(null ?? undefined ?? 'x'); // 'x' — chaining ?? with itself is legal
console.log(0 ?? 5 ? 'yes' : 'no'); // 'no' — parses as (0 ?? 5) ? ... : ...go deeper
Recognise the error message and know the fix: add parentheses around either a ?? b or b || c, because JavaScript will not let you mix ?? with || or && unparenthesised.
Explain why the grammar forbids it — the falsy and nullish tests differ — and walk a value like 0 through both groupings to show they produce different results.
Treat the parenthesisation as a design decision rather than a syntax chore: state explicitly which values in that expression count as missing, and prefer normalising the option at the boundary over stacking two fallback rules inline.
Be able to discuss how a language committee handles ambiguous precedence generally — erroring rather than guessing keeps a new operator from silently changing the meaning of code, a tradeoff worth citing when you set conventions for your own APIs and DSLs.
## The rule In the ECMAScript grammar, the production for `??` is written so that its operands cannot themselves be an unparenthesised logical AND or OR expression. The practical consequence is a hard parse error: ```js // All SyntaxError: a ?? b || c; a || b ?? c; a && b ?? c; a ?? b && c; ``` This is a *syntax* error, not a runtime one — the whole script or module fails to parse, so nothing in the file runs. ## Why the language refuses to guess `||` returns its right operand for any falsy left operand; `??` does so only for `null` and `undefined`. Because the two tests are genuinely different, either precedence choice would produce an expression whose meaning a reader is likely to get wrong. Consider `a ?? b || c` with `a = 0`: - as `(a ?? b) || c`: `0 ?? b` is `0`, then `0 || c` is `c` - as `a ?? (b || c)`: `0` is not nullish, so the result is `0` One grouping keeps the zero and the other throws it away. Rather than have half the readership assume the wrong one, the grammar requires parentheses. ## The two fixes and what they mean ```js const v = 0; console.log((v ?? 10) || 'fallback'); // 'fallback' console.log(v ?? (10 || 'fallback')); // 0 // console.log(v ?? 10 || 'fallback'); // SyntaxError ``` `(a ?? b) || c` is "resolve absence first, then treat the result's falsiness as a second failure mode". `a ?? (b || c)` is "if `a` is present use it; otherwise compute a default that itself has a falsy fallback". In real code the second shape is the more common intent, e.g. `config.title ?? (envTitle || 'Untitled')`. ## What is allowed The restriction is narrow — it applies only to mixing with `&&` and `||`. ```js a ?? b ?? c; // fine: ?? is left-associative and chains x.y?.z ?? 'default'; // fine: optional chaining composes freely a ?? b + 1; // fine: parses as a ?? (b + 1) a ?? b === c; // fine: parses as a ?? (b === c) cond ? a ?? b : c; // fine a ?? b ? c : d; // fine: parses as (a ?? b) ? c : d ``` Arithmetic, comparison and equality all bind tighter than `??`, so they group on the right-hand side as you would expect. The conditional (ternary) operator binds *looser*, so an unparenthesised `??` on the left of a `?` becomes the condition. That last one is worth reading twice, because it is easy to misread `a ?? b ? c : d` as `a ?? (b ? c : d)` — it is not. ## Interaction with logical assignment The same intent shows up in the assignment forms: `x ??= y` and `x ||= y` are separate operators, and there is no way to mix their tests in one statement either. If you need both rules, write the two steps out. ## Interaction with tooling Because this is a parse error, it fails loudly and early — you will see it the first time the file is loaded, not on some rare code path. That makes it one of the friendlier errors in the language: the fix is always mechanical, and choosing the parentheses forces you to answer the real question of whether zero and the empty string should count as missing. ## What to say in an interview Name the rule (mixing `??` with `&&`/`||` unparenthesised is a SyntaxError), explain the motivation (the two operators test different things, so implicit precedence would mislead), and show that you know the two groupings are not equivalent by walking a falsy-but-not-nullish example such as `0` through both.
- How does `??` bind relative to the conditional operator?Tighter. `a ?? b ? c : d` parses as `(a ?? b) ? c : d`, so the coalesced value becomes the condition. That is a common misreading, since people expect the ternary to be the right operand of `??`. If you want that, parenthesise: `a ?? (b ? c : d)`.
- Is `a ?? b ?? c` legal, and how does it evaluate?Legal and left-associative: it behaves as `(a ?? b) ?? c`, returning the first operand that is neither null nor undefined, and short-circuiting so later operands are not evaluated once one is found. The restriction only forbids mixing `??` with `&&` or `||` without parentheses.
- Why did the language choose an error rather than assigning a precedence?Because the operators test different conditions — falsy versus nullish — the two possible groupings can give different results for values like `0` and `''`. Any silent precedence would leave a large fraction of readers assuming the other one, so the grammar makes the author write the parentheses and commit to a meaning.
saying these in an interview costs you the question
- Thinks it is a runtime TypeError rather than a parse error
- Says ?? simply has lower precedence than || so it just works
- Claims the two parenthesised forms are equivalent
- Believes ?? cannot be chained with itself either
- Reads a ?? b ? c : d as a ?? (b ? c : d)