Universal and conditional catch blocks with the new when keyword

Proposal: GitHub - rafageist/proposal-universal-catch-and-when: Expands JavaScript's error-handling by allowing any block to have catch and finally blocks with optional when clauses for conditional error handling.

This proposal expands JavaScript's current try-catch mechanism by allowing any code block to have an optional catch and finally block. The catch block can also be enhanced with a when clause, enabling conditional error handling directly within the block. This approach maintains consistency with JavaScript's existing syntax and structure, offering a natural extension of control flow mechanisms without introducing new, complex constructs.

/* 
any block of code: try, anonymous, functions, 
if, do, class, catch, finally, switch, ... 
*/ 

{
    // Code that may throw an error
    // ...
} 
catch (<var>) when (<boolean expression) 
{
    // Error-handling logic
    // ...
    // catch is also a block!
}
catch (<var>) when (<boolean expression>) 
{
    // Error-handling logic
    // ...
}
finally 
{
    // Cleanup code, executed regardless of success or failure

    // ... but finally is also a block!
} 
catch (<var>) when (<boolean expression>) 
{
    // Error-handling logic
    // ...
}
...

Examples

Anonymous block

{
  doSomething();
} 
catch(err) when (err instanceof Error)
{
  console.log("Error: ", err);
}
catch(err) when (err instanceof TypeError)
{
  console.log("TypeError: ", err);
}
finally
{
  console.log("Finally block");
}

Function block

async function loadData() 
{
	return await fetch("https://api.example.com");
}
catch (err)
{
   	console.log("Error fetch data");
}

If and else block

if (condition == false)
{
	console.log("Condition is false");
} 
catch(err) when (err instanceof Error)
{
	console.log("Error condition");
}
else
{
	console.log("Condition is true");
}
catch(err) when (err instanceof TypeError)
{
	console.log("Error in else block");
}
finally
{
	console.log("Finally block after if-else");
}

For loop block

for (let i = 0; i < 10; i++)
{
	console.log(i);
}
catch (err)
{
	console.log("Error in loop");
}

Switch block

switch (condition)
{
	case 1:
		console.log("Case 1");
		break;
	case 2:
		console.log("Case 2");
		break;
	default:
		console.log("Default case");
}
catch (err)
{
	console.log("Error in switch");
}

Class block

class MyClass
{
	constructor()
	{
		console.log("MyClass constructor");
	}
}
catch (err)
{
	console.log("Error in class");
}

... and more!

This proposal is currently in need of a champion to help refine and present it to the TC39 committee. If you're interested in advocating for more flexible and structured error handling in JavaScript, your support would be invaluable. For more details, please read the proposal in the repository.

3 Likes

Related threads:

2 Likes

Please do not use ChatGPT to write posts on this or any other forum.

2 Likes

Ok. I haven't done it and I won't do it, because it's not necessary. Thanks for letting me know.

Do you have any questions about the proposal? Any suggestions?

I have made a major update to my proposal. Now the body of the catch can also be optional. The variable that catches the error can be treated in the scope of the catch

{ var result = await process() } catch (err);

console.log(result);

if (err) console.log(err);

This will allow for more flexibility in error catching, among other improvements to code readability.

{ await process() } catch;

Another update: I have removed the Class catch, Object literal catch and Switch catch examples, as they do not have a Block definition. This remains to be analyzed.

Update:

The concept of differentiating statement blocks and declarative blocks has been refined. A new issue has been opened for further analysis.

elegant solution - i love it. @rafageist , is a constructor(){} catch{} also supported?

1 Like

That would make currently valid code ambiguous.

try { ... } catch
(e) // is this capturing `e`, or an expression statement after catch without capture&block?
{ ... } // is this error handling, or block statement after catch(e) without block?

Changes meaning of currently valid code.

try { ... }
catch (e)
{ ... }
console.log(e) // e from outer scope

Thanks ReinsBrain

Yes, a catch{} block can be used inside a constructor because the constructor's body is an imperative block (like a function).

class MyClass {
  constructor() {
    {
      throw new Error("Something went wrong");
    } catch (err) {
      console.log("Error handled in constructor:", err.message);
    }
  }
}

Thank you, lightmare! This has already been addressed in the proposal (section: Importance of semi-colons). If the catch body is omitted, the syntax must explicitly use catch;, avoiding ambiguity. For example:

try {
  throw new Error("Error in block");
} catch; // Explicitly ends the catch
(1 + 1);

Additionally, semicolons are recommended to clearly separate statements, ensuring there’s no confusion for the interpreter or developers.