Skip to content

tc39/proposal-throw-expressions

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

  • Ron Buckton (@rbuckton)

Proposal

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

  • Parameter initializers
    functionsave(filename=thrownewTypeError("Argument required")){}
  • Arrow function bodies
    lint(ast,{with: ()=>thrownewError("avoid using 'with' statements.")});
  • Conditional expressions
    functiongetEncoder(encoding){constencoder=encoding==="utf8" ? newUTF8Encoder() : encoding==="utf16le" ? newUTF16Encoder(false) : encoding==="utf16be" ? newUTF16Encoder(true) : thrownewError("Unsupported encoding");}
  • Logical operations
    classProduct{getid(){returnthis._id;}setid(value){this._id=value||thrownewError("Invalid value");}}

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:

throwa ? b : c;// evaluates 'a' and throws either 'b' or 'c'(throwa ? b : c);// without restriction would throw 'a', so `?` is forbiddenthrowa,b;// evaluates 'a', throws 'b'(throwa,b);// would throw 'a', not 'b', so `,` is forbiddenthrowa&&b;// throws 'a' if 'a' is falsy, otherwise throws 'b'(throwa&&b);// would always throw 'a', so `&&` is forbiddenthrowa||b;// throws 'a' if 'a' is truthy, otherwise throws 'b'(throwa||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:

constx=a ? throwb : 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=>{throwerr;};// via helper...functiongetEncoder1(encoding){constencoder=encoding==="utf8" ? newUTF8Encoder() : encoding==="utf16le" ? newUTF16Encoder(false) : encoding==="utf16be" ? newUTF16Encoder(true) : __throw(newError("Unsupported encoding"));}// via arrow...functiongetEncoder2(encoding){constencoder=encoding==="utf8" ? newUTF8Encoder() : encoding==="utf16le" ? newUTF16Encoder(false) : encoding==="utf16be" ? newUTF16Encoder(true) : (()=>{thrownewError("Unsupported encoding");})();}

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

  • The __throw helper will appear in err.stack in a host environment.
    • This can be mitigated in some hosts that have Error.captureStackTrace
  • Hosts require more information for optimization/deoptimization decisions as the throw is not local to the function.
  • Not ergonomic for debugging as the frame where the exception is raised is inside of the helper.
  • Inline invoked arrow not ergonomic (at least 10 more symbols compared to native).

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

  • Identified a "champion" who will advance the addition.
  • Prose outlining the problem or need and the general shape of a solution.
  • Illustrative examples of usage.
  • High-level API(proposal does not introduce an API).

Stage 2 Entrance Criteria

Stage 3 Entrance Criteria

Stage 4 Entrance Criteria

  • Test262 acceptance tests have been written for mainline usage scenarios and merged.
  • Two compatible implementations which pass the acceptance tests: [1], [2].
  • A pull request has been sent to tc39/ecma262 with the integrated spec text.
  • The ECMAScript editor has signed off on the pull request.

About

Proposal for ECMAScript 'throw' expressions

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors 2

  •  
  •