Home

Awesome

ECMAScript throw expressions

This proposal defines new syntax to throw exceptions from within an expression context.

Status

Stage: 2
Champion: Ron Buckton (@rbuckton)

For more information see the TC39 proposal process.

Authors

Proposal

A throw expression allows you to throw exceptions in expression contexts. For example:

A throw expression does not replace a throw statement due to the difference in the precedence of their values. To maintain the precedence of the throw statement, we must add a lookahead restriction to ExpressionStatement to avoid ambiguity.

Due to the difference in precedence between a throw expression and a ThrowStatement, certain operators to the right of the expression would parse differently between the two which could cause ambiguity and confusion:

throw a ? b : c; // evaluates 'a' and throws either 'b' or 'c'
(throw a ? b : c); // without restriction would throw 'a', so `?` is forbidden

throw a, b; // evaluates 'a', throws 'b'
(throw a, b); // would throw 'a', not 'b', so `,` is forbidden

throw a && b; // throws 'a' if 'a' is falsy, otherwise throws 'b'
(throw a && b); // would always throw 'a', so `&&` is forbidden

throw a || b; // throws 'a' if 'a' is truthy, otherwise throws 'b'
(throw a || b); // would always throw 'a', so `||` is forbidden

// ... etc.

As a result, all binary operators and the ? operator are forbidden to the right of a throw expression. To use these operators inside of a throw expression, the expression must be surrounded with parentheses:

(throw (a, b)); // evaluates 'a', throws 'b'
(throw (a ? b : c)); // evaluates 'a' and throws either 'b' or 'c'

However, we do not forbid : so that a throw expression can still be easily used in a ternary:

const x = a ? throw b : c; // if 'a' then throw 'b', else evaluate 'c'

Grammar

++ThrowExpressionInvalidPunctuator : one of
  `,` `<` `>` `<=` `>=` `==` `!=` `===` `!==` `+` `-` `*` `/` `%` `**` `<<` `>>` `>>>` `&` `|` `^` `&&` `||` `??`
  `=` `+=` `-=` `*=` `%=` `**=` `<<=` `>>=` `>>>=` `&=` `|=` `^=` `&&=` `||=` `??=` `?`

  UnaryExpression[Yield, Await] :
++  `throw` UnaryExpression[?Yield, ?Await] [lookahead ∉ ThrowExpressionInvalidPunctuator]

  ExpressionStatement[Yield, Await] :
--  [lookahead ∉ {`{`, `function`, `async` [no |LineTerminator| here] `function`, `class`, `let [`}] Expression[+In, ?Yield, ?Await] `;`
++  [lookahead ∉ {`{`, `function`, `async` [no |LineTerminator| here] `function`, `class`, `let [`, `throw`}] Expression[+In, ?Yield, ?Await] `;`

Other Notes

A throw expression can be approximated in ECMAScript using something like the following definition:

const __throw = err => { throw err; };

// via helper...
function getEncoder1(encoding) {
  const encoder = encoding === "utf8" ? new UTF8Encoder() 
                : encoding === "utf16le" ? new UTF16Encoder(false) 
                : encoding === "utf16be" ? new UTF16Encoder(true) 
                : __throw(new Error("Unsupported encoding"));
}

// via arrow...
function getEncoder2(encoding) {
  const encoder = encoding === "utf8" ? new UTF8Encoder() 
                : encoding === "utf16le" ? new UTF16Encoder(false) 
                : encoding === "utf16be" ? new UTF16Encoder(true) 
                : (() => { throw new Error("Unsupported encoding"); })();
}

However, this has several downsides compared to a native implementation:

Resources

TODO

The following is a high-level list of tasks to progress through each stage of the TC39 proposal process:

Stage 1 Entrance Criteria

Stage 2 Entrance Criteria

Stage 3 Entrance Criteria

Stage 4 Entrance Criteria

<!-- The following are shared links used throughout the README: -->