Overview
better-result provides robust error handling through:- TaggedError - Factory for creating discriminated error classes
- Exhaustive matching - Type-safe error handling with
matchError() - UnhandledException - Wrapper for unexpected exceptions
- Panic - Unrecoverable errors (defects in user code)
TaggedError Factory
Creating Tagged Errors
TaggedError is a factory function that creates error classes with a _tag discriminator for exhaustive type checking.
Why TaggedError?
TaggedError provides several benefits over plain Error objects:Type-safe discrimination
Type-safe discrimination
The TypeScript enforces that all error variants are handled.
_tag property enables exhaustive pattern matching:Structured error data
Structured error data
Store domain-specific information:
Cause chaining
Cause chaining
TaggedError automatically chains Error causes in stack traces:
JSON serialization
JSON serialization
TaggedError instances serialize cleanly:
Type Signature
Exhaustive Error Matching
matchError()
Exhaustively pattern match on a union of tagged errors. TypeScript enforces that all variants are handled.If you remove a handler, TypeScript will produce a compile error. This ensures all error cases are handled.
Data-Last (Pipeable) API
matchErrorPartial()
Partial pattern match with a fallback for unhandled errors.Type Narrowing in Fallback
The fallback parameter is typed asExclude<E, HandledErrors>, providing type safety:
Error Recovery Patterns
Mapping Errors to Default Values
Converting Errors to Success
Retrying on Specific Errors
UnhandledException
UnhandledException wraps exceptions caught by Result.try() and Result.tryPromise() when no custom catch handler is provided.
When to Use UnhandledException
- Use UnhandledException
- Use Custom Errors
When you want quick error wrapping without defining custom error types:
Panic: Unrecoverable Errors
Panic represents defects in user code - situations where recovery is impossible:
- Callback throws inside
map(),andThen(),match(), etc. catchhandler throws inResult.try()orResult.tryPromise()finallyblock throws inResult.gen()- Generator body throws before yielding
Symbol.disposeorSymbol.asyncDisposethrows
Why Panic?
Unlike recoverable errors (wrapped inErr), Panics indicate programming errors that shouldn’t be caught and handled - they should be fixed.
Panic in Generators
Avoiding Panics
Type Guards
TaggedError.is()
Every TaggedError class has a staticis() type guard:
isTaggedError()
Check if a value is any TaggedError instance:isPanic()
Check if a value is a Panic:Best Practices
Use TaggedError for domain errors
Use TaggedError for domain errors
Create discriminated error unions for your domain:
Use exhaustive matching
Use exhaustive matching
Prefer
matchError() over if-else chains:Don't catch Panics
Don't catch Panics
Let Panics crash the application - they indicate bugs:
Include context in errors
Include context in errors
Store enough information to debug issues:
Next Steps
Pattern Matching
Learn how to handle Results with match() and type narrowing
Generator Composition
Master Result.gen() for imperative-style error handling