errore 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -180,11 +180,13 @@ async function processOrder(orderId: string): Promise<OrderError | Receipt> {
180
180
  // Caller gets union of all possible errors
181
181
  const receipt = await processOrder('123')
182
182
  if (isError(receipt)) {
183
- matchError(receipt, {
183
+ const message = matchError(receipt, {
184
184
  NotFoundError: e => `Order ${e.id} not found`,
185
185
  ValidationError: e => `Invalid: ${e.field}`,
186
186
  PaymentError: e => `Payment failed: ${e.reason}`,
187
187
  })
188
+ console.log(message)
189
+ return
188
190
  }
189
191
  ```
190
192
 
@@ -229,15 +231,25 @@ class NetworkError extends TaggedError('NetworkError')<{
229
231
  type AppError = ValidationError | NetworkError
230
232
 
231
233
  // Exhaustive matching (TypeScript ensures all cases handled)
232
- matchError(error, {
234
+ const message = matchError(error, {
233
235
  ValidationError: e => `Invalid ${e.field}`,
234
236
  NetworkError: e => `Failed to fetch ${e.url}`
235
237
  })
238
+ console.log(message)
239
+
240
+ // Handle plain Error with _ (underscore) handler
241
+ function riskyOp(): ValidationError | Error { ... }
242
+ const err = riskyOp()
243
+ const msg = matchError(err, {
244
+ ValidationError: e => `Invalid ${e.field}`,
245
+ _: e => `Plain error: ${e.message}` // catches non-tagged Error
246
+ })
236
247
 
237
248
  // Partial matching with fallback
238
- matchErrorPartial(error, {
249
+ const fallbackMsg = matchErrorPartial(error, {
239
250
  ValidationError: e => `Invalid ${e.field}`
240
251
  }, e => `Unknown error: ${e.message}`)
252
+ console.log(fallbackMsg)
241
253
 
242
254
  // Type guards
243
255
  ValidationError.is(value) // specific class
package/dist/index.d.mts CHANGED
@@ -71,23 +71,34 @@ declare const TaggedError: {
71
71
  */
72
72
  declare const isTaggedError: (value: unknown) => value is AnyTaggedError;
73
73
  /**
74
- * Handler map for exhaustive matching
74
+ * Handler map that includes `_` for plain Error (untagged)
75
75
  */
76
- type MatchHandlers<E extends AnyTaggedError, R> = {
77
- [K in E['_tag']]: (err: Extract<E, {
76
+ type MatchHandlersWithPlain<E extends Error, R> = {
77
+ [K in Extract<E, AnyTaggedError>['_tag']]: (err: Extract<E, {
78
78
  _tag: K;
79
79
  }>) => R;
80
- };
80
+ } & (Exclude<E, AnyTaggedError> extends never ? {} : {
81
+ _: (err: Exclude<E, AnyTaggedError>) => R;
82
+ });
81
83
  /**
82
- * Exhaustive pattern match on tagged error union by _tag.
84
+ * Exhaustive pattern match on error union by _tag.
85
+ * Use `_` handler for plain Error instances without _tag.
83
86
  *
84
87
  * @example
88
+ * // Tagged errors only
85
89
  * matchError(err, {
86
90
  * NotFoundError: (e) => `Missing: ${e.id}`,
87
91
  * ValidationError: (e) => `Invalid: ${e.field}`,
88
92
  * });
93
+ *
94
+ * @example
95
+ * // Mixed tagged and plain Error
96
+ * matchError(err, {
97
+ * NotFoundError: (e) => `Missing: ${e.id}`,
98
+ * _: (e) => `Unknown error: ${e.message}`,
99
+ * });
89
100
  */
90
- declare function matchError<E extends AnyTaggedError, R>(err: E, handlers: MatchHandlers<E, R>): R;
101
+ declare function matchError<E extends Error, R>(err: E, handlers: MatchHandlersWithPlain<E, R>): R;
91
102
  /**
92
103
  * Partial pattern match with fallback for unhandled tags.
93
104
  *
@@ -96,7 +107,7 @@ declare function matchError<E extends AnyTaggedError, R>(err: E, handlers: Match
96
107
  * NotFoundError: (e) => `Missing: ${e.id}`,
97
108
  * }, (e) => `Unknown: ${e.message}`);
98
109
  */
99
- declare function matchErrorPartial<E extends AnyTaggedError, R>(err: E, handlers: Partial<MatchHandlers<E, R>>, fallback: (e: E) => R): R;
110
+ declare function matchErrorPartial<E extends Error, R>(err: E, handlers: Partial<MatchHandlersWithPlain<E, R>>, fallback: (e: E) => R): R;
100
111
  declare const UnhandledError_base: TaggedErrorClass<"UnhandledError", {
101
112
  message: string;
102
113
  cause: unknown;
package/dist/index.d.ts CHANGED
@@ -71,23 +71,34 @@ declare const TaggedError: {
71
71
  */
72
72
  declare const isTaggedError: (value: unknown) => value is AnyTaggedError;
73
73
  /**
74
- * Handler map for exhaustive matching
74
+ * Handler map that includes `_` for plain Error (untagged)
75
75
  */
76
- type MatchHandlers<E extends AnyTaggedError, R> = {
77
- [K in E['_tag']]: (err: Extract<E, {
76
+ type MatchHandlersWithPlain<E extends Error, R> = {
77
+ [K in Extract<E, AnyTaggedError>['_tag']]: (err: Extract<E, {
78
78
  _tag: K;
79
79
  }>) => R;
80
- };
80
+ } & (Exclude<E, AnyTaggedError> extends never ? {} : {
81
+ _: (err: Exclude<E, AnyTaggedError>) => R;
82
+ });
81
83
  /**
82
- * Exhaustive pattern match on tagged error union by _tag.
84
+ * Exhaustive pattern match on error union by _tag.
85
+ * Use `_` handler for plain Error instances without _tag.
83
86
  *
84
87
  * @example
88
+ * // Tagged errors only
85
89
  * matchError(err, {
86
90
  * NotFoundError: (e) => `Missing: ${e.id}`,
87
91
  * ValidationError: (e) => `Invalid: ${e.field}`,
88
92
  * });
93
+ *
94
+ * @example
95
+ * // Mixed tagged and plain Error
96
+ * matchError(err, {
97
+ * NotFoundError: (e) => `Missing: ${e.id}`,
98
+ * _: (e) => `Unknown error: ${e.message}`,
99
+ * });
89
100
  */
90
- declare function matchError<E extends AnyTaggedError, R>(err: E, handlers: MatchHandlers<E, R>): R;
101
+ declare function matchError<E extends Error, R>(err: E, handlers: MatchHandlersWithPlain<E, R>): R;
91
102
  /**
92
103
  * Partial pattern match with fallback for unhandled tags.
93
104
  *
@@ -96,7 +107,7 @@ declare function matchError<E extends AnyTaggedError, R>(err: E, handlers: Match
96
107
  * NotFoundError: (e) => `Missing: ${e.id}`,
97
108
  * }, (e) => `Unknown: ${e.message}`);
98
109
  */
99
- declare function matchErrorPartial<E extends AnyTaggedError, R>(err: E, handlers: Partial<MatchHandlers<E, R>>, fallback: (e: E) => R): R;
110
+ declare function matchErrorPartial<E extends Error, R>(err: E, handlers: Partial<MatchHandlersWithPlain<E, R>>, fallback: (e: E) => R): R;
100
111
  declare const UnhandledError_base: TaggedErrorClass<"UnhandledError", {
101
112
  message: string;
102
113
  cause: unknown;
package/dist/index.js CHANGED
@@ -93,13 +93,30 @@ Caused by: ${indented}`;
93
93
  );
94
94
  var isTaggedError = isAnyTaggedError;
95
95
  function matchError(err, handlers) {
96
- const handler = handlers[err._tag];
97
- return handler(err);
96
+ const h = handlers;
97
+ if ("_tag" in err && typeof err._tag === "string") {
98
+ const handler = h[err._tag];
99
+ if (handler) {
100
+ return handler(err);
101
+ }
102
+ }
103
+ const fallbackHandler = h["_"];
104
+ if (fallbackHandler) {
105
+ return fallbackHandler(err);
106
+ }
107
+ throw new Error(`No handler for error: ${err.message}`);
98
108
  }
99
109
  function matchErrorPartial(err, handlers, fallback) {
100
- const handler = handlers[err._tag];
101
- if (handler) {
102
- return handler(err);
110
+ const h = handlers;
111
+ if ("_tag" in err && typeof err._tag === "string") {
112
+ const handler = h[err._tag];
113
+ if (handler) {
114
+ return handler(err);
115
+ }
116
+ }
117
+ const underscoreHandler = h["_"];
118
+ if (underscoreHandler) {
119
+ return underscoreHandler(err);
103
120
  }
104
121
  return fallback(err);
105
122
  }
package/dist/index.mjs CHANGED
@@ -48,13 +48,30 @@ Caused by: ${indented}`;
48
48
  );
49
49
  var isTaggedError = isAnyTaggedError;
50
50
  function matchError(err, handlers) {
51
- const handler = handlers[err._tag];
52
- return handler(err);
51
+ const h = handlers;
52
+ if ("_tag" in err && typeof err._tag === "string") {
53
+ const handler = h[err._tag];
54
+ if (handler) {
55
+ return handler(err);
56
+ }
57
+ }
58
+ const fallbackHandler = h["_"];
59
+ if (fallbackHandler) {
60
+ return fallbackHandler(err);
61
+ }
62
+ throw new Error(`No handler for error: ${err.message}`);
53
63
  }
54
64
  function matchErrorPartial(err, handlers, fallback) {
55
- const handler = handlers[err._tag];
56
- if (handler) {
57
- return handler(err);
65
+ const h = handlers;
66
+ if ("_tag" in err && typeof err._tag === "string") {
67
+ const handler = h[err._tag];
68
+ if (handler) {
69
+ return handler(err);
70
+ }
71
+ }
72
+ const underscoreHandler = h["_"];
73
+ if (underscoreHandler) {
74
+ return underscoreHandler(err);
58
75
  }
59
76
  return fallback(err);
60
77
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errore",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Type-safe errors as values for TypeScript. Like Go, but with full type inference.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",