Result vs throw
You may have encountered opinions like: "Since JavaScript can throw errors anywhere and it's impossible to manage everything with Result, there's no point in introducing Result at all."
However, we don't necessarily agree with this perspective. The key insight is that Result should only handle "anticipated errors" - there's no need to wrap every possible error in a Result.
The Philosophy: Anticipated vs Unexpected Errors
The distinction between what should be handled with Result versus what should be allowed to throw lies in understanding the nature of the error:
Anticipated Errors (Use Result)
These are errors that are part of your application's business logic and should be handled explicitly:
Unexpected Errors (Let them throw)
These are infrastructure-level or truly unexpected errors:
- Database connection failures
- Network timeouts
- Out of memory errors
- Unknown exceptions
These should be allowed to throw and be caught by infrastructure-level error handling (like Sentry).
When You Need Better Stack Traces: Using Result.try
However, when you want more detailed stack traces for debugging purposes, we recommend using Result.try to wrap unexpected errors with custom error classes. This approach gives you application-level stack traces instead of library-level ones.
Defining Custom Error Classes
First, define a custom error class for unexpected errors:
For more details about @praha/error-factory, see the Custom Error page.
Using Result.try for Better Error Handling
Benefits of This Approach
- Clean Stack Traces: You get stack traces that point to your application code, not deep into library internals
- Error Context: You can add meaningful context to errors while preserving the original error
- Consistent Error Handling: All errors, whether anticipated or unexpected, can be handled through the Result interface when needed
- Debugging: The original error is still accessible through the
causeproperty for debugging purposes
Conclusion
The goal isn't to eliminate all throws in favor of Result, but to use each approach where it's most appropriate. Result excels at handling expected, business-level errors that require explicit handling, while throw remains the right choice for unexpected system errors that should be handled at the infrastructure level.
This hybrid approach gives you the benefits of explicit error handling where it matters most, without the burden of wrapping every possible error in your application.
