go-go-try 7.1.0 → 7.2.1

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/dist/index.d.mts CHANGED
@@ -1,6 +1,51 @@
1
1
  type Success<T> = readonly [undefined, T];
2
2
  type Failure<E> = readonly [E, undefined];
3
3
  type Result<E, T> = Success<T> | Failure<E>;
4
+ /**
5
+ * Base interface for tagged errors.
6
+ * The `_tag` property enables discriminated union narrowing.
7
+ */
8
+ interface TaggedError<T extends string> {
9
+ readonly _tag: T;
10
+ readonly message: string;
11
+ readonly cause?: unknown;
12
+ }
13
+ /**
14
+ * Creates a tagged error class for discriminated error handling.
15
+ *
16
+ * @template T The literal type of the tag
17
+ * @param tag The string tag to identify this error type (e.g., 'DatabaseError')
18
+ * @returns A class constructor for creating tagged errors
19
+ *
20
+ * @example
21
+ * const DatabaseError = taggedError('DatabaseError')
22
+ * const NetworkError = taggedError('NetworkError')
23
+ *
24
+ * type MyError = InstanceType<typeof DatabaseError> | InstanceType<typeof NetworkError>
25
+ *
26
+ * function fetchUser(id: string): Result<MyError, User> {
27
+ * const [err, user] = goTryRaw(fetch(`/users/${id}`), DatabaseError)
28
+ * if (err) return failure(err)
29
+ * // ...
30
+ * }
31
+ *
32
+ * // Pattern matching on errors
33
+ * if (err._tag === 'DatabaseError') {
34
+ * // TypeScript knows this is DatabaseError
35
+ * }
36
+ */
37
+ declare function taggedError<T extends string>(tag: T): {
38
+ new (message: string, options?: {
39
+ cause?: unknown;
40
+ }): {
41
+ readonly _tag: T;
42
+ readonly cause?: unknown;
43
+ name: string;
44
+ message: string;
45
+ stack?: string;
46
+ };
47
+ isError(error: unknown): error is Error;
48
+ };
4
49
  type ResultWithDefault<E, T> = readonly [E | undefined, T];
5
50
  type MaybePromise<T> = T | Promise<T>;
6
51
  interface GoTryAllOptions {
@@ -121,6 +166,28 @@ declare function goTry<T>(fn: () => Promise<T>): Promise<Result<string, T>>;
121
166
  declare function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>;
122
167
  declare function goTry<T>(fn: () => T): Result<string, T>;
123
168
  declare function goTry<T>(value: T): Result<string, T>;
169
+ /**
170
+ * Type for error constructors that can be used with goTryRaw.
171
+ */
172
+ type ErrorConstructor<E> = new (message: string, options?: {
173
+ cause?: unknown;
174
+ }) => E;
175
+ /**
176
+ * Creates a union type from multiple tagged error classes.
177
+ *
178
+ * @template T A tuple of tagged error class types
179
+ * @returns A union of all instance types
180
+ *
181
+ * @example
182
+ * const DatabaseError = taggedError('DatabaseError')
183
+ * const NetworkError = taggedError('NetworkError')
184
+ *
185
+ * type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError]>
186
+ * // Equivalent to: DatabaseError | NetworkError
187
+ */
188
+ type TaggedUnion<T extends readonly ErrorConstructor<unknown>[]> = {
189
+ [K in keyof T]: T[K] extends ErrorConstructor<infer E> ? E : never;
190
+ }[number];
124
191
  /**
125
192
  * Executes a function, promise, or value and returns a Result type.
126
193
  * If an error occurs, it returns a Failure with the raw error object.
@@ -128,6 +195,7 @@ declare function goTry<T>(value: T): Result<string, T>;
128
195
  * @template T The type of the successful result
129
196
  * @template E The type of the error, defaults to Error
130
197
  * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
198
+ * @param {ErrorConstructor<E>} [ErrorClass] - Optional error constructor to wrap caught errors
131
199
  * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
132
200
  *
133
201
  * @example
@@ -141,13 +209,24 @@ declare function goTry<T>(value: T): Result<string, T>;
141
209
  * @example
142
210
  * // With a promise
143
211
  * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
212
+ *
213
+ * @example
214
+ * // With tagged error for discriminated unions
215
+ * const DatabaseError = taggedError('DatabaseError');
216
+ * const [err, result] = await goTryRaw(fetchData(), DatabaseError);
217
+ * // err is InstanceType<typeof DatabaseError> | undefined
144
218
  */
145
219
  declare function goTryRaw<T, E = Error>(fn: () => never): Result<E, never>;
220
+ declare function goTryRaw<T, E = Error>(fn: () => never, ErrorClass: ErrorConstructor<E>): Result<E, never>;
146
221
  declare function goTryRaw<T, E = Error>(fn: () => Promise<T>): Promise<Result<E, T>>;
222
+ declare function goTryRaw<T, E = Error>(fn: () => Promise<T>, ErrorClass: ErrorConstructor<E>): Promise<Result<E, T>>;
147
223
  declare function goTryRaw<T, E = Error>(promise: Promise<T>): Promise<Result<E, T>>;
224
+ declare function goTryRaw<T, E = Error>(promise: Promise<T>, ErrorClass: ErrorConstructor<E>): Promise<Result<E, T>>;
148
225
  declare function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
226
+ declare function goTryRaw<T, E = Error>(fn: () => T, ErrorClass: ErrorConstructor<E>): Result<E, T>;
149
227
  declare function goTryRaw<T, E = Error>(value: T): Result<E, T>;
228
+ declare function goTryRaw<T, E = Error>(value: T, ErrorClass: ErrorConstructor<E>): Result<E, T>;
150
229
 
151
- export { failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success };
152
- export type { Failure, GoTryAllOptions, MaybePromise, Result, ResultWithDefault, Success };
230
+ export { failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
231
+ export type { ErrorConstructor, Failure, GoTryAllOptions, MaybePromise, Result, ResultWithDefault, Success, TaggedError, TaggedUnion };
153
232
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sources":["../src/index.ts"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"index.d.mts","sources":["../src/index.ts"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,13 @@
1
+ function taggedError(tag) {
2
+ return class TaggedErrorClass extends Error {
3
+ constructor(message, options) {
4
+ super(message);
5
+ this._tag = tag;
6
+ this.name = tag;
7
+ this.cause = options?.cause;
8
+ }
9
+ };
10
+ }
1
11
  function isSuccess(result) {
2
12
  return result[0] === void 0;
3
13
  }
@@ -116,25 +126,31 @@ function goTry(value) {
116
126
  return failure(getErrorMessage(err));
117
127
  }
118
128
  }
119
- function goTryRaw(value) {
129
+ function goTryRaw(value, ErrorClass) {
130
+ const wrapError = (err) => {
131
+ if (ErrorClass) {
132
+ if (err === void 0) {
133
+ return new ErrorClass("undefined");
134
+ }
135
+ if (isError(err)) {
136
+ return new ErrorClass(err.message, { cause: err });
137
+ }
138
+ return new ErrorClass(String(err));
139
+ }
140
+ if (err === void 0) {
141
+ return new Error("undefined");
142
+ }
143
+ return isError(err) ? err : new Error(String(err));
144
+ };
120
145
  try {
121
146
  const result = typeof value === "function" ? value() : value;
122
147
  if (isPromise(result)) {
123
- return result.then((resolvedValue) => success(resolvedValue)).catch((err) => {
124
- if (err === void 0) {
125
- return failure(new Error("undefined"));
126
- }
127
- return failure(
128
- isError(err) ? err : new Error(String(err))
129
- );
130
- });
148
+ return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(wrapError(err)));
131
149
  }
132
150
  return success(result);
133
151
  } catch (err) {
134
- return failure(
135
- isError(err) ? err : new Error(String(err))
136
- );
152
+ return failure(wrapError(err));
137
153
  }
138
154
  }
139
155
 
140
- export { failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success };
156
+ export { failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-go-try",
3
- "version": "7.1.0",
3
+ "version": "7.2.1",
4
4
  "description": "Tries to execute a sync/async function, returns a result tuple",
5
5
  "license": "MIT",
6
6
  "repository": "thelinuxlich/go-go-try",
package/src/index.test.ts CHANGED
@@ -1,7 +1,15 @@
1
1
  import { attest } from '@ark/attest'
2
2
  import { assert, describe, test } from 'vitest'
3
+
4
+ // Helper for exhaustive switch checks - if this function is called,
5
+ // it means we forgot to handle a case in a switch statement
6
+ function assertNever(value: never): never {
7
+ throw new Error(`Unhandled case: ${String(value)}`)
8
+ }
3
9
  import {
4
10
  type Result,
11
+ type TaggedUnion,
12
+ failure,
5
13
  goTry,
6
14
  goTryAll,
7
15
  goTryAllRaw,
@@ -9,6 +17,8 @@ import {
9
17
  goTryRaw,
10
18
  isFailure,
11
19
  isSuccess,
20
+ success,
21
+ taggedError,
12
22
  } from './index.js'
13
23
 
14
24
  test(`value returned by callback is used when callback doesn't throw`, async () => {
@@ -944,3 +954,399 @@ describe('goTryAllRaw', () => {
944
954
  assert.deepEqual(results, [])
945
955
  })
946
956
  })
957
+
958
+
959
+ describe('taggedError', () => {
960
+ test('creates error class with _tag property', () => {
961
+ const DatabaseError = taggedError('DatabaseError')
962
+ const err = new DatabaseError('connection failed')
963
+
964
+ assert.equal(err._tag, 'DatabaseError')
965
+ assert.equal(err.message, 'connection failed')
966
+ assert.equal(err.name, 'DatabaseError')
967
+ assert.ok(err instanceof Error)
968
+ assert.ok(err instanceof DatabaseError)
969
+ })
970
+
971
+ test('supports cause option', () => {
972
+ const NetworkError = taggedError('NetworkError')
973
+ const cause = new Error('ECONNREFUSED')
974
+ const err = new NetworkError('request failed', { cause })
975
+
976
+ assert.equal(err._tag, 'NetworkError')
977
+ assert.equal(err.message, 'request failed')
978
+ assert.equal(err.cause, cause)
979
+ })
980
+
981
+ test('different error types have distinct _tag values', () => {
982
+ const DatabaseError = taggedError('DatabaseError')
983
+ const NetworkError = taggedError('NetworkError')
984
+ const ValidationError = taggedError('ValidationError')
985
+
986
+ const dbErr = new DatabaseError('db fail')
987
+ const netErr = new NetworkError('net fail')
988
+ const valErr = new ValidationError('val fail')
989
+
990
+ assert.equal(dbErr._tag, 'DatabaseError')
991
+ assert.equal(netErr._tag, 'NetworkError')
992
+ assert.equal(valErr._tag, 'ValidationError')
993
+ })
994
+
995
+ test('works with goTryRaw for discriminated error handling', async () => {
996
+ const DatabaseError = taggedError('DatabaseError')
997
+ const NetworkError = taggedError('NetworkError')
998
+
999
+ // Simulate a database operation that might fail
1000
+ const fetchFromDb = () => {
1001
+ throw new DatabaseError('query failed')
1002
+ }
1003
+
1004
+ // Simulate a network request that might fail
1005
+ const fetchFromNetwork = () => {
1006
+ throw new NetworkError('timeout')
1007
+ }
1008
+
1009
+ // Wrap in functions so goTryRaw can catch the errors
1010
+ const [dbErr, dbResult] = goTryRaw(fetchFromDb, DatabaseError)
1011
+ const [netErr, netResult] = goTryRaw(fetchFromNetwork, NetworkError)
1012
+
1013
+ // Type narrowing via discriminated union
1014
+ if (dbErr) {
1015
+ assert.equal(dbErr._tag, 'DatabaseError')
1016
+ assert.equal(dbErr.message, 'query failed')
1017
+ }
1018
+
1019
+ if (netErr) {
1020
+ assert.equal(netErr._tag, 'NetworkError')
1021
+ assert.equal(netErr.message, 'timeout')
1022
+ }
1023
+
1024
+ assert.equal(dbResult, undefined)
1025
+ assert.equal(netResult, undefined)
1026
+ })
1027
+
1028
+ test('can return union of error types from function', async () => {
1029
+ const DatabaseError = taggedError('DatabaseError')
1030
+ const NetworkError = taggedError('NetworkError')
1031
+
1032
+ type AppError = InstanceType<typeof DatabaseError> | InstanceType<typeof NetworkError>
1033
+
1034
+ async function fetchUser(id: string): Promise<Result<AppError, { id: string; name: string }>> {
1035
+ const [dbErr, user] = await goTryRaw(
1036
+ Promise.resolve({ id, name: 'John' }),
1037
+ DatabaseError,
1038
+ )
1039
+ if (dbErr) return failure<AppError>(dbErr)
1040
+ return [undefined, user] as const
1041
+ }
1042
+
1043
+ async function fetchData(): Promise<Result<AppError, string>> {
1044
+ const [netErr, data] = await goTryRaw(Promise.resolve('data'), NetworkError)
1045
+ if (netErr) return failure<AppError>(netErr)
1046
+ return [undefined, data] as const
1047
+ }
1048
+
1049
+ // Use the functions
1050
+ const [userErr, user] = await fetchUser('123')
1051
+ const [, data] = await fetchData()
1052
+
1053
+ // Type narrowing works with discriminated unions
1054
+ if (userErr) {
1055
+ // userErr._tag can be 'DatabaseError' | 'NetworkError'
1056
+ assert.ok(userErr._tag === 'DatabaseError' || userErr._tag === 'NetworkError')
1057
+ }
1058
+
1059
+ assert.deepEqual(user, { id: '123', name: 'John' })
1060
+ assert.equal(data, 'data')
1061
+ })
1062
+
1063
+ test('pattern matching on error types', async () => {
1064
+ const DatabaseError = taggedError('DatabaseError')
1065
+ const NetworkError = taggedError('NetworkError')
1066
+ const ValidationError = taggedError('ValidationError')
1067
+
1068
+ type AppError =
1069
+ | InstanceType<typeof DatabaseError>
1070
+ | InstanceType<typeof NetworkError>
1071
+ | InstanceType<typeof ValidationError>
1072
+
1073
+ function handleError(err: AppError): string {
1074
+ switch (err._tag) {
1075
+ case 'DatabaseError':
1076
+ return `DB: ${err.message}`
1077
+ case 'NetworkError':
1078
+ return `NET: ${err.message}`
1079
+ case 'ValidationError':
1080
+ return `VAL: ${err.message}`
1081
+ default:
1082
+ return `UNK: ${err.message}`
1083
+ }
1084
+ }
1085
+
1086
+ assert.equal(handleError(new DatabaseError('fail')), 'DB: fail')
1087
+ assert.equal(handleError(new NetworkError('timeout')), 'NET: timeout')
1088
+ assert.equal(handleError(new ValidationError('invalid')), 'VAL: invalid')
1089
+ })
1090
+
1091
+ test('exhaustive switch with never type', () => {
1092
+ const DatabaseError = taggedError('DatabaseError')
1093
+ const NetworkError = taggedError('NetworkError')
1094
+ const ValidationError = taggedError('ValidationError')
1095
+
1096
+ type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError, typeof ValidationError]>
1097
+
1098
+ // Exhaustive switch - TypeScript will error if any case is missing
1099
+ function handleErrorExhaustive(err: AppError): string {
1100
+ switch (err._tag) {
1101
+ case 'DatabaseError':
1102
+ return `DB: ${err.message}`
1103
+ case 'NetworkError':
1104
+ return `NET: ${err.message}`
1105
+ case 'ValidationError':
1106
+ return `VAL: ${err.message}`
1107
+ default:
1108
+ // This ensures all cases are handled - if we forget a case above,
1109
+ // err will not be never and TypeScript will error
1110
+ return assertNever(err)
1111
+ }
1112
+ }
1113
+
1114
+ assert.equal(handleErrorExhaustive(new DatabaseError('fail')), 'DB: fail')
1115
+ assert.equal(handleErrorExhaustive(new NetworkError('timeout')), 'NET: timeout')
1116
+ assert.equal(handleErrorExhaustive(new ValidationError('invalid')), 'VAL: invalid')
1117
+ })
1118
+ })
1119
+
1120
+ describe('taggedError type tests', () => {
1121
+ test('error class has correct type structure', () => {
1122
+ const DatabaseError = taggedError('DatabaseError')
1123
+ const err = new DatabaseError('fail')
1124
+
1125
+ // The error should have _tag, message, and cause properties
1126
+ attest<'DatabaseError'>(err._tag)
1127
+ attest<string>(err.message)
1128
+ attest<unknown | undefined>(err.cause)
1129
+ // Error instance check - the class extends Error
1130
+ assert.ok(err instanceof Error)
1131
+ })
1132
+
1133
+ test('discriminated union type narrowing works', () => {
1134
+ const DatabaseError = taggedError('DatabaseError')
1135
+ const NetworkError = taggedError('NetworkError')
1136
+
1137
+ type AppError = InstanceType<typeof DatabaseError> | InstanceType<typeof NetworkError>
1138
+
1139
+ const err: AppError = new DatabaseError('fail')
1140
+
1141
+ // Type narrowing via _tag
1142
+ if (err._tag === 'DatabaseError') {
1143
+ attest<InstanceType<typeof DatabaseError>>(err)
1144
+ } else {
1145
+ attest<InstanceType<typeof NetworkError>>(err)
1146
+ }
1147
+ })
1148
+ })
1149
+
1150
+
1151
+ describe('TaggedUnion type helper', () => {
1152
+ test('creates union type from multiple error classes', () => {
1153
+ const DatabaseError = taggedError('DatabaseError')
1154
+ const NetworkError = taggedError('NetworkError')
1155
+ const ValidationError = taggedError('ValidationError')
1156
+
1157
+ // Create union type using helper
1158
+ type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError, typeof ValidationError]>
1159
+
1160
+ // All error types should be assignable to AppError
1161
+ const dbErr: AppError = new DatabaseError('db fail')
1162
+ const netErr: AppError = new NetworkError('net fail')
1163
+ const valErr: AppError = new ValidationError('val fail')
1164
+
1165
+ assert.equal(dbErr._tag, 'DatabaseError')
1166
+ assert.equal(netErr._tag, 'NetworkError')
1167
+ assert.equal(valErr._tag, 'ValidationError')
1168
+
1169
+ // Pattern matching should work
1170
+ function handleError(err: AppError): string {
1171
+ switch (err._tag) {
1172
+ case 'DatabaseError':
1173
+ return `DB: ${err.message}`
1174
+ case 'NetworkError':
1175
+ return `NET: ${err.message}`
1176
+ case 'ValidationError':
1177
+ return `VAL: ${err.message}`
1178
+ default:
1179
+ return `UNK: ${err.message}`
1180
+ }
1181
+ }
1182
+
1183
+ assert.equal(handleError(dbErr), 'DB: db fail')
1184
+ assert.equal(handleError(netErr), 'NET: net fail')
1185
+ assert.equal(handleError(valErr), 'VAL: val fail')
1186
+ })
1187
+
1188
+ test('works with goTryRaw for typed error handling', async () => {
1189
+ const DatabaseError = taggedError('DatabaseError')
1190
+ const NetworkError = taggedError('NetworkError')
1191
+
1192
+ type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError]>
1193
+
1194
+ async function fetchData(): Promise<Result<AppError, string>> {
1195
+ const [err, data] = await goTryRaw(
1196
+ Promise.reject(new Error('timeout')),
1197
+ NetworkError,
1198
+ )
1199
+ if (err) return failure<AppError>(err)
1200
+ return [undefined, data] as const
1201
+ }
1202
+
1203
+ const [err, data] = await fetchData()
1204
+ assert.equal(data, undefined)
1205
+ assert.equal(err?._tag, 'NetworkError')
1206
+ assert.equal(err?.message, 'timeout')
1207
+ })
1208
+ })
1209
+
1210
+
1211
+ describe('inferred return types with tagged errors', () => {
1212
+ test('function infers union return type without explicit annotation', async () => {
1213
+ const DatabaseError = taggedError('DatabaseError')
1214
+ const NetworkError = taggedError('NetworkError')
1215
+
1216
+ // No explicit return type - TypeScript should infer it
1217
+ async function fetchUserData(id: string) {
1218
+ // First operation might fail with DatabaseError
1219
+ const [dbErr, user] = await goTryRaw(
1220
+ Promise.resolve({ id, name: 'John' }),
1221
+ DatabaseError,
1222
+ )
1223
+ if (dbErr) return failure(dbErr)
1224
+
1225
+ // Second operation might fail with NetworkError
1226
+ const [netErr, enriched] = await goTryRaw(
1227
+ Promise.resolve({ ...user!, email: 'john@example.com' }),
1228
+ NetworkError,
1229
+ )
1230
+ if (netErr) return failure(netErr)
1231
+
1232
+ return [undefined, enriched] as const
1233
+ }
1234
+
1235
+ // The return type should be inferred as:
1236
+ // Promise<Result<DatabaseError | NetworkError, { id: string; name: string; email: string }>>
1237
+
1238
+ const [err, user] = await fetchUserData('123')
1239
+
1240
+ // Type narrowing should work
1241
+ if (err) {
1242
+ // err should be DatabaseError | NetworkError
1243
+ switch (err._tag) {
1244
+ case 'DatabaseError':
1245
+ assert.equal(err._tag, 'DatabaseError')
1246
+ break
1247
+ case 'NetworkError':
1248
+ assert.equal(err._tag, 'NetworkError')
1249
+ break
1250
+ default:
1251
+ assertNever(err)
1252
+ }
1253
+ } else {
1254
+ // user should be fully typed
1255
+ assert.deepEqual(user, { id: '123', name: 'John', email: 'john@example.com' })
1256
+ }
1257
+ })
1258
+
1259
+ test('sync function infers union return type', () => {
1260
+ const ParseError = taggedError('ParseError')
1261
+ const ValidateError = taggedError('ValidateError')
1262
+
1263
+ // No explicit return type annotation
1264
+ function processConfig(input: string) {
1265
+ // Parse step
1266
+ const [parseErr, parsed] = goTryRaw(() => JSON.parse(input), ParseError)
1267
+ if (parseErr) return failure(parseErr)
1268
+
1269
+ // Validate step
1270
+ const [validateErr, validated] = goTryRaw(() => {
1271
+ if (!parsed!.port) throw new Error('Missing port')
1272
+ return parsed as { port: number }
1273
+ }, ValidateError)
1274
+ if (validateErr) return failure(validateErr)
1275
+
1276
+ return [undefined, validated] as const
1277
+ }
1278
+
1279
+ // Success case
1280
+ const [err1, config1] = processConfig('{"port": 3000}')
1281
+ assert.equal(err1, undefined)
1282
+ assert.deepEqual(config1, { port: 3000 })
1283
+
1284
+ // Parse error case
1285
+ const [err2, config2] = processConfig('invalid json')
1286
+ assert.equal(err2?._tag, 'ParseError')
1287
+ assert.equal(config2, undefined)
1288
+
1289
+ // Validation error case
1290
+ const [err3, config3] = processConfig('{"host": "localhost"}')
1291
+ assert.equal(err3?._tag, 'ValidateError')
1292
+ assert.equal(config3, undefined)
1293
+ })
1294
+
1295
+ test('multiple error sources collapse to union type', async () => {
1296
+ const ErrorA = taggedError('ErrorA')
1297
+ const ErrorB = taggedError('ErrorB')
1298
+ const ErrorC = taggedError('ErrorC')
1299
+
1300
+ // Function with multiple potential error sources
1301
+ async function complexOperation(shouldFail: 'a' | 'b' | 'c' | 'none') {
1302
+ const [errA, valA] = await goTryRaw(
1303
+ shouldFail === 'a' ? Promise.reject(new Error('a')) : Promise.resolve('step1'),
1304
+ ErrorA,
1305
+ )
1306
+ if (errA) return failure(errA)
1307
+
1308
+ const [errB, valB] = await goTryRaw(
1309
+ shouldFail === 'b' ? Promise.reject(new Error('b')) : Promise.resolve('step2'),
1310
+ ErrorB,
1311
+ )
1312
+ if (errB) return failure(errB)
1313
+
1314
+ const [errC, valC] = await goTryRaw(
1315
+ shouldFail === 'c' ? Promise.reject(new Error('c')) : Promise.resolve('step3'),
1316
+ ErrorC,
1317
+ )
1318
+ if (errC) return failure(errC)
1319
+
1320
+ return success({ valA, valB, valC })
1321
+ }
1322
+
1323
+ // All cases should be handled with exhaustive switch
1324
+ async function handleOperation(shouldFail: 'a' | 'b' | 'c' | 'none') {
1325
+ const [err, result] = await complexOperation(shouldFail)
1326
+
1327
+ if (err) {
1328
+ // TypeScript should infer err as ErrorA | ErrorB | ErrorC
1329
+ switch (err._tag) {
1330
+ case 'ErrorA':
1331
+ return `Failed at step A: ${err.message}`
1332
+ case 'ErrorB':
1333
+ return `Failed at step B: ${err.message}`
1334
+ case 'ErrorC':
1335
+ return `Failed at step C: ${err.message}`
1336
+ default:
1337
+ return assertNever(err)
1338
+ }
1339
+ }
1340
+
1341
+ return `Success: ${JSON.stringify(result)}`
1342
+ }
1343
+
1344
+ assert.equal(await handleOperation('a'), 'Failed at step A: a')
1345
+ assert.equal(await handleOperation('b'), 'Failed at step B: b')
1346
+ assert.equal(await handleOperation('c'), 'Failed at step C: c')
1347
+ assert.equal(
1348
+ await handleOperation('none'),
1349
+ 'Success: {"valA":"step1","valB":"step2","valC":"step3"}',
1350
+ )
1351
+ })
1352
+ })