This roadmap outlines potential features and improvements for ts-typed-errors, inspired by ts-pattern and other pattern matching libraries.
- π΄ High Priority - High impact, relatively easy to implement
- π‘ Medium Priority - Good value but requires more work
- π’ Low Priority - Nice to have, less critical
Inspiration: ts-pattern's isMatching()
Description: Create reusable type guards for error types with optional predicates.
API:
import { isErrorOf } from 'ts-typed-errors';
const NetworkError = defineError('NetworkError')<{ status: number; url: string }>();
// Simple type guard
const isNetworkError = isErrorOf(NetworkError);
// Type guard with predicate
const isServerError = isErrorOf(NetworkError, (e) => e.data.status >= 500);
const isClientError = isErrorOf(NetworkError, (e) => e.data.status >= 400 && e.data.status < 500);
// Usage in code
if (isServerError(error)) {
// TypeScript knows error is NetworkError with status >= 500
console.log(`Server error: ${error.data.status}`);
}Benefits:
- Reusable type guards
- Composable error checks
- Better code organization
- Reduced duplication
Implementation complexity: Low Impact: High
Inspiration: ts-pattern's P.select()
Description: Extract specific properties from error data directly in the handler.
API Option 1: Method chaining
matchErrorOf<Err>(error)
.select(NetworkError, 'status', (status) => `Retry with status: ${status}`)
.select(ParseError, 'at', (location) => `Parse failed at: ${location}`)
.exhaustive();API Option 2: Handler parameter
matchErrorOf<Err>(error)
.with(NetworkError, { select: 'status' }, (status) => `Status: ${status}`)
.with(ParseError, { select: ['at', 'message'] }, ({ at, message }) => `${message} at ${at}`)
.exhaustive();API Option 3: Inline extraction
matchErrorOf<Err>(error)
.with(NetworkError, (e) => e.data.status, (status) => `Status: ${status}`)
.exhaustive();Benefits:
- Cleaner handler signatures
- Direct access to needed properties
- Less verbose code
- Better composability
Implementation complexity: Medium Impact: High
Inspiration: ts-pattern's P.not()
Description: Match all cases except specific error types.
API:
matchErrorOf<Err>(error)
.withNot(NetworkError, (e) => `Handle any error except NetworkError`)
.exhaustive();
// Or with multiple exclusions
matchError(error)
.withNot([NetworkError, ParseError], (e) => `Neither network nor parse error`)
.otherwise((e) => 'Fallback');Benefits:
- Handle "everything except X" scenarios
- Reduce boilerplate for common cases
- More expressive API
Implementation complexity: Low Impact: Medium
Inspiration: ts-pattern's multiple patterns in single .with()
Description: Match multiple error types with the same handler.
API:
matchErrorOf<Err>(error)
.withAny([NetworkError, TimeoutError], (e) => `Connection issue: ${e.message}`)
.with(ParseError, (e) => `Parse error at ${e.data.at}`)
.exhaustive();Benefits:
- DRY principle
- Handle similar errors together
- Cleaner code for common handlers
Implementation complexity: Low Impact: Medium
Inspiration: ts-pattern's P.shape() and P.array()
Description: Pattern match on complex error structures (useful for aggregate errors).
API:
const AggregateError = defineError('AggregateError')<{ errors: Error[] }>();
const ValidationError = defineError('ValidationError')<{ fields: string[] }>();
matchErrorOf<AppError>(error)
.with(AggregateError, {
shape: {
errors: (errs) => errs.every(e => e instanceof ValidationError)
}
}, (e) => 'All validation errors')
.with(AggregateError, {
shape: {
errors: (errs) => errs.some(e => e instanceof NetworkError)
}
}, (e) => 'Contains network errors')
.exhaustive();Benefits:
- Handle nested error structures
- Match on array patterns
- Support for aggregate errors
- More powerful matching capabilities
Implementation complexity: High Impact: Medium
Description: Improve IDE autocomplete and hover documentation.
Implementation:
- Add more detailed JSDoc comments
- Include usage examples in JSDoc
- Document common patterns
- Add
@exampletags to all public APIs
Benefits:
- Better developer experience
- Faster onboarding
- Less context switching to docs
Implementation complexity: Low Impact: High
Description: Transform errors before matching.
API:
matchErrorOf<Err>(error)
.map((e) => {
// Add context or transform before matching
return e instanceof NetworkError
? new EnrichedNetworkError(e.message, { ...e.data, timestamp: Date.now() })
: e;
})
.with(EnrichedNetworkError, (e) => `Error at ${e.data.timestamp}`)
.exhaustive();Benefits:
- Pre-process errors
- Add context before matching
- Transform error types
Implementation complexity: Medium Impact: Low
Description: Allow partial matching with default handlers for groups.
API:
matchError(error)
.withDefault([NetworkError, TimeoutError], (e) => 'Connection issue')
.with(NetworkError, { status: 404 }, (e) => 'Not found')
.with(NetworkError, { status: 500 }, (e) => 'Server error')
.otherwise((e) => 'Unknown error');Benefits:
- Default handlers for error groups
- Override specific cases
- Fallback behavior
Implementation complexity: Medium Impact: Low
Description: Utilities to compose and combine errors.
API:
import { combineErrors, isAnyOf, isAllOf } from 'ts-typed-errors';
// Check if error is one of several types
if (isAnyOf(error, [NetworkError, TimeoutError])) {
// Handle connection errors
}
// Combine multiple errors
const combined = combineErrors([error1, error2, error3]);
// Create error hierarchies
const DatabaseError = defineError('DatabaseError')<{ query: string }>();
const ConnectionError = DatabaseError.extend('ConnectionError')<{ host: string }>();Benefits:
- Better error organization
- Error hierarchies
- Bulk error handling
Implementation complexity: High Impact: Medium
Description: Support for async handlers in match expressions.
API:
await matchErrorOf<Err>(error)
.with(NetworkError, async (e) => {
await logToService(e);
return 'Logged network error';
})
.with(ParseError, async (e) => {
await notifyAdmin(e);
return 'Notified admin';
})
.exhaustive();Benefits:
- Native async/await support
- Cleaner async error handling
- No need for wrapper functions
Implementation complexity: Medium Impact: Medium
Description: Built-in patterns for common error recovery strategies.
API:
import { retry, fallback, ignore } from 'ts-typed-errors';
matchErrorOf<Err>(error)
.with(NetworkError, retry(3, exponentialBackoff))
.with(ValidationError, fallback(defaultValue))
.with(ParseError, ignore())
.exhaustive();Benefits:
- Common recovery patterns
- Less boilerplate
- Standardized error handling
Implementation complexity: High Impact: Low
Description: Optimize matching performance for large error unions.
Ideas:
- Cache instanceof checks
- Use lookup tables for tag-based matching
- Lazy evaluation of predicates
- Benchmark and optimize hot paths
Benefits:
- Faster error matching
- Better runtime performance
- Scalable for large projects
Implementation complexity: Medium Impact: Low (unless performance is critical)
Description: Automatically propagate context through error chains.
API:
const withContext = (context: Record<string, any>) => {
return {
wrap: (fn: Function) => {
// Automatically add context to all thrown errors
}
};
};
const { wrap } = withContext({ requestId: '123', userId: 'abc' });
const result = await wrap(riskyOperation)();
// All errors will have requestId and userId in their contextBenefits:
- Automatic context tracking
- Better debugging
- Request tracing
Implementation complexity: High Impact: Medium
Description: Allow extensions via plugins.
API:
import { plugin } from 'ts-typed-errors';
plugin.register('logger', {
onMatch: (error, handler) => {
console.log(`Matched ${error.tag}`);
},
onMiss: (error) => {
console.log(`No match for ${error}`);
}
});Benefits:
- Extensibility
- Third-party integrations
- Custom behaviors
Implementation complexity: High Impact: Low
Description: Utilities for serializing/deserializing errors.
API:
import { serialize, deserialize } from 'ts-typed-errors';
// Serialize for API responses
const serialized = serialize(error);
// { tag: 'NetworkError', message: '...', data: {...}, stack: '...' }
// Deserialize from API
const error = deserialize(serialized, [NetworkError, ParseError]);Benefits:
- API error handling
- Error logging
- Error transmission
Implementation complexity: Medium Impact: Medium
- β
isErrorOf()type guard builder - Reusable type guards with optional predicates - β
withNot()negation pattern - Match everything except specified types - β
withAny()multiple pattern matching - Match multiple types with same handler - β Better IntelliSense documentation - Comprehensive JSDoc on all public APIs
- β
Property selection (
select()) - Type-safe property extraction from error data - β
Error composition utilities -
isAnyOf,isAllOffor composing type guards - β
Async error matching -
matchErrorAsync,matchErrorOfAsyncwith native async/await β οΈ Complex structure matching - DEFERRED (low priority, high complexity)
- β
Error serialization -
serialize,deserialize,toJSON,fromJSONutilities - β
Error transformation -
map()to transform errors before matching - β Performance optimizations - Tag-based lookup tables for O(1) matching
β οΈ Error context propagation - DEFERRED (complex, requires more design work)
- π Plugin system - Allow extensions via plugins
- π Error recovery patterns - Built-in retry, fallback, ignore patterns
- π Framework integrations - Express, Next.js, Hono middleware
Have ideas for other features? Open an issue or PR on GitHub!
This roadmap is subject to change based on:
- Community feedback
- Real-world usage patterns
- TypeScript language updates
- Performance considerations
Last updated: October 16, 2025 Current version: v0.3.0