@@ -33,10 +33,15 @@ if (!res.ok) {
3333## ✨ Features
3434
3535- ** 🎯 Exhaustive matching** - TypeScript enforces that you handle all error types
36- - ** 🔧 Ergonomic API** - Declarative ` matchError ` / ` matchErrorOf ` chains with ` .select() ` for property extraction
37- - ** 📦 Tiny & fast** - ~ 1–2 kB, zero dependencies, works everywhere
36+ - ** 🔧 Ergonomic API** - Declarative ` matchError ` / ` matchErrorOf ` chains with:
37+ - ` .select() ` for property extraction
38+ - ` .withAny() ` for matching multiple types
39+ - ` .withNot() ` for negation patterns
40+ - ` .when() ` for predicate matching
41+ - ** 📦 Tiny & fast** - ~ 2 kB, zero dependencies, works everywhere
3842- ** 🛡️ Type-safe** - Full TypeScript support with strict type checking
3943- ** 🔄 Result pattern** - Convert throwing functions to ` Result<T, E> ` types
44+ - ** 🔨 Composable guards** - Reusable type guards with ` isErrorOf() `
4045
4146## 🚀 Quick Start
4247
@@ -168,6 +173,39 @@ const message = matchErrorOf<AllErrors>(error)
168173 .exhaustive (); // ✅ Compiler error if any case missing
169174```
170175
176+ #### ` matchErrorAsync(error) ` & ` matchErrorOfAsync<AllErrors>(error) `
177+ Async versions with native async/await support for all handlers.
178+
179+ ``` ts
180+ // Free-form async matching
181+ const result = await matchErrorAsync (error )
182+ .with (NetworkError , async (err ) => {
183+ await logToService (err );
184+ return ` Logged network error: ${err .data .status } ` ;
185+ })
186+ .with (ParseError , async (err ) => {
187+ await notifyAdmin (err );
188+ return ` Notified admin about parse error ` ;
189+ })
190+ .otherwise (async (err ) => ` Unknown error: ${err } ` );
191+
192+ // Exhaustive async matching
193+ const result = await matchErrorOfAsync <AllErrors >(error )
194+ .with (NetworkError , async (err ) => {
195+ await retryRequest (err );
196+ return ' retried' ;
197+ })
198+ .with (ValidationError , async (err ) => {
199+ await validateAndLog (err );
200+ return ' validation' ;
201+ })
202+ .with (ParseError , async (err ) => {
203+ await fixData (err );
204+ return ' fixed' ;
205+ })
206+ .exhaustive (); // ✅ All cases handled
207+ ```
208+
171209### Advanced Matching
172210
173211#### ` .select(constructor, key, handler) `
@@ -196,6 +234,45 @@ matchError(error)
196234- Type-safe property extraction
197235- Works with exhaustive matching
198236
237+ #### ` .withAny(constructors, handler) `
238+ Match multiple error types with the same handler.
239+
240+ ``` ts
241+ const NetworkError = defineError (' NetworkError' )<{ status: number }>();
242+ const TimeoutError = defineError (' TimeoutError' )<{ duration: number }>();
243+ const ParseError = defineError (' ParseError' )<{ at: string }>();
244+
245+ matchErrorOf <Err >(error )
246+ .withAny ([NetworkError , TimeoutError ], (e ) => ' Connection issue - retry' )
247+ .with (ParseError , (e ) => ` Parse error at ${e .data .at } ` )
248+ .exhaustive ();
249+ ```
250+
251+ ** Benefits:**
252+ - DRY principle - avoid duplicating handlers
253+ - Group similar error types together
254+ - Cleaner code for common error handling
255+
256+ #### ` .withNot(constructor | constructors, handler) `
257+ Match all errors except the specified types.
258+
259+ ``` ts
260+ // Exclude single type
261+ matchError (error )
262+ .withNot (NetworkError , (e ) => ' Not a network error' )
263+ .otherwise (() => ' Network error' );
264+
265+ // Exclude multiple types
266+ matchError (error )
267+ .withNot ([NetworkError , ParseError ], (e ) => ' Neither network nor parse error' )
268+ .otherwise ((e ) => ' Fallback' );
269+ ```
270+
271+ ** Benefits:**
272+ - Handle "everything except X" scenarios
273+ - Reduce boilerplate for common cases
274+ - More expressive API
275+
199276### Utility Functions
200277
201278#### ` isError(value) `
@@ -207,13 +284,133 @@ if (isError(value)) {
207284}
208285```
209286
210- #### ` hasCode(error, code) `
211- Check if an error has a specific error code.
287+ #### ` hasCode(code) `
288+ Creates a type guard for errors with a specific error code.
289+
290+ ``` ts
291+ const isDNSError = hasCode (' ENOTFOUND' );
292+ const isPermissionError = hasCode (' EACCES' );
293+
294+ if (isDNSError (error )) {
295+ // Handle DNS error - TypeScript knows error.code is 'ENOTFOUND'
296+ }
297+
298+ // Use in pattern matching
299+ matchError (error )
300+ .with (hasCode (' ENOTFOUND' ), (err ) => ' DNS lookup failed' )
301+ .with (hasCode (' EACCES' ), (err ) => ' Permission denied' )
302+ .otherwise ((err ) => ' Other error' );
303+ ```
304+
305+ #### ` isErrorOf(constructor, predicate?) `
306+ Creates reusable type guards for specific error types with optional predicates.
212307
213308``` ts
214- if (hasCode (error , ' ENOTFOUND' )) {
215- // Handle DNS error
309+ const NetworkError = defineError (' NetworkError' )<{ status: number ; url: string }>();
310+
311+ // Simple type guard
312+ const isNetworkError = isErrorOf (NetworkError );
313+ if (isNetworkError (error )) {
314+ console .log (error .data .status ); // TypeScript knows this is NetworkError
315+ }
316+
317+ // Type guard with predicate
318+ const isServerError = isErrorOf (NetworkError , (e ) => e .data .status >= 500 );
319+ const isClientError = isErrorOf (NetworkError , (e ) => e .data .status >= 400 && e .data .status < 500 );
320+
321+ if (isServerError (error )) {
322+ console .log (` Server error: ${error .data .status } ` );
216323}
324+
325+ // Use in pattern matching
326+ matchError (error )
327+ .with (isServerError , (e ) => ' Retry server error' )
328+ .with (isClientError , (e ) => ' Handle client error' )
329+ .otherwise (() => ' Other error' );
330+ ```
331+
332+ #### ` isAnyOf(error, constructors) `
333+ Checks if an error is an instance of any of the provided error constructors.
334+
335+ ``` ts
336+ const NetworkError = defineError (' NetworkError' )<{ status: number }>();
337+ const TimeoutError = defineError (' TimeoutError' )<{ duration: number }>();
338+
339+ if (isAnyOf (error , [NetworkError , TimeoutError ])) {
340+ // Handle connection-related errors
341+ console .log (' Connection issue detected' );
342+ }
343+
344+ // More concise than:
345+ if (error instanceof NetworkError || error instanceof TimeoutError ) {
346+ // ...
347+ }
348+ ```
349+
350+ #### ` isAllOf(value, guards) `
351+ Checks if a value matches all of the provided type guards.
352+
353+ ``` ts
354+ const NetworkError = defineError (' NetworkError' )<{ status: number ; url: string }>();
355+
356+ const isServerError = isErrorOf (NetworkError , (e ) => e .data .status >= 500 );
357+ const hasRetryableStatus = (e : unknown ): e is any =>
358+ isError (e ) && ' status' in e && [502 , 503 , 504 ].includes ((e as any ).status );
359+
360+ if (isAllOf (error , [isServerError , hasRetryableStatus ])) {
361+ // Error is both a server error AND has a retryable status
362+ console .log (' Retrying server error' );
363+ }
364+ ```
365+
366+ ### Serialization
367+
368+ #### ` serialize(error, includeStack?) `
369+ Serializes an error to a JSON-safe object for transmission or storage.
370+
371+ ``` ts
372+ const error = new NetworkError (' Request failed' , { status: 500 , url: ' /api' });
373+ const serialized = serialize (error );
374+ // {
375+ // tag: 'NetworkError',
376+ // message: 'Request failed',
377+ // name: 'NetworkError',
378+ // data: { status: 500, url: '/api' },
379+ // stack: '...'
380+ // }
381+
382+ // Send over network
383+ await fetch (' /api/log' , {
384+ method: ' POST' ,
385+ body: JSON .stringify (serialized )
386+ });
387+ ```
388+
389+ #### ` deserialize(serialized, constructors) `
390+ Deserializes a plain object back into an error instance.
391+
392+ ``` ts
393+ // Receive from API
394+ const response = await fetch (' /api/errors/123' );
395+ const serialized = await response .json ();
396+
397+ // Deserialize with known constructors
398+ const error = deserialize (serialized , [NetworkError , ParseError ]);
399+
400+ if (error instanceof NetworkError ) {
401+ console .log (` Network error: ${error .data .status } ` ); // Type-safe!
402+ }
403+ ```
404+
405+ #### ` toJSON(error) ` & ` fromJSON(json, constructors) `
406+ Convenience functions combining serialization with JSON stringify/parse.
407+
408+ ``` ts
409+ // Convert to JSON string
410+ const json = toJSON (error );
411+
412+ // Parse from JSON string
413+ const restored = fromJSON (json , [NetworkError , ParseError ]);
217414```
218415
219416## 🎯 Advanced Examples
0 commit comments