go-go-try 6.0.5 → 6.1.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
@@ -46,7 +46,7 @@ const [err, todos = []] = await goTry(fetchTodos()) // err is string | undefined
46
46
  if (err) sendToErrorTrackingService(err)
47
47
 
48
48
  // goTry extracts the error message from the error object, if you want the raw error object, use goTryRaw
49
- const [err, value] = goTryRaw(() => JSON.parse('{/}')) // err will be Error, value will be always T
49
+ const [err, value] = goTryRaw(() => JSON.parse('{/}')) // err is Error | undefined, value is T | undefined
50
50
 
51
51
  // fetch todos, fallback to empty array, send error to your error tracking service
52
52
  const [err, todos = []] = await goTryRaw(fetchTodos()) // err is Error | undefined
@@ -59,11 +59,7 @@ if (err) sendToErrorTrackingService(err)
59
59
 
60
60
  - synchronous/asynchronous function / Promise
61
61
 
62
- **Returns** a tuple with the possible error and result (Golang style)
62
+ **Returns** a tuple with the possible error and result as `[Err | undefined, T | undefined]` (Golang style)
63
63
 
64
64
 
65
65
  If you use TypeScript, the types are well defined and won't let you make a mistake.
66
-
67
- ## Inspiration
68
-
69
- - This library started as a fork of [good-try](https://github.com/astoilkov/good-try) but diverged a lot so I decided to rename it
package/dist/index.cjs CHANGED
@@ -13,6 +13,7 @@ function failure(error) {
13
13
  return [error, void 0];
14
14
  }
15
15
  function getErrorMessage(error) {
16
+ if (error === void 0) return "undefined";
16
17
  if (typeof error === "string") return error;
17
18
  if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
18
19
  return error.message;
@@ -26,6 +27,9 @@ function getErrorMessage(error) {
26
27
  function isPromise(value) {
27
28
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
28
29
  }
30
+ function isError(value) {
31
+ return value instanceof Error;
32
+ }
29
33
  function goTry(value) {
30
34
  try {
31
35
  const result = typeof value === "function" ? value() : value;
@@ -41,11 +45,20 @@ function goTryRaw(value) {
41
45
  try {
42
46
  const result = typeof value === "function" ? value() : value;
43
47
  if (isPromise(result)) {
44
- return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(err));
48
+ return result.then((resolvedValue) => success(resolvedValue)).catch((err) => {
49
+ if (err === void 0) {
50
+ return failure(new Error("undefined"));
51
+ }
52
+ return failure(
53
+ isError(err) ? err : new Error(String(err))
54
+ );
55
+ });
45
56
  }
46
57
  return success(result);
47
58
  } catch (err) {
48
- return failure(err);
59
+ return failure(
60
+ isError(err) ? err : new Error(String(err))
61
+ );
49
62
  }
50
63
  }
51
64
 
package/dist/index.d.cts CHANGED
@@ -6,9 +6,50 @@ declare function isSuccess<E, T>(result: Result<E, T>): result is Success<T>;
6
6
  declare function isFailure<E, T>(result: Result<E, T>): result is Failure<E>;
7
7
  declare function success<T>(value: T): Success<T>;
8
8
  declare function failure<E>(error: E): Failure<E>;
9
+ /**
10
+ * Executes a function, promise, or value and returns a Result type.
11
+ * If an error occurs, it returns a Failure with the error message as a string.
12
+ *
13
+ * @template T The type of the successful result
14
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
15
+ * @returns {Result<string, T> | Promise<Result<string, T>>} A Result type or a Promise of a Result type
16
+ *
17
+ * @example
18
+ * // With a value
19
+ * const [err, result] = goTry(42);
20
+ *
21
+ * @example
22
+ * // With a function
23
+ * const [err, result] = goTry(() => JSON.parse('{"key": "value"}'));
24
+ *
25
+ * @example
26
+ * // With a promise
27
+ * const [err, result] = await goTry(fetch('https://api.example.com/data'));
28
+ */
9
29
  declare function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>;
10
30
  declare function goTry<T>(fn: () => T): Result<string, T>;
11
31
  declare function goTry<T>(value: T): Result<string, T>;
32
+ /**
33
+ * Executes a function, promise, or value and returns a Result type.
34
+ * If an error occurs, it returns a Failure with the raw error object.
35
+ *
36
+ * @template T The type of the successful result
37
+ * @template E The type of the error, defaults to Error
38
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
39
+ * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
40
+ *
41
+ * @example
42
+ * // With a value
43
+ * const [err, result] = goTryRaw(42);
44
+ *
45
+ * @example
46
+ * // With a function
47
+ * const [err, result] = goTryRaw(() => JSON.parse('{"key": "value"}'));
48
+ *
49
+ * @example
50
+ * // With a promise
51
+ * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
52
+ */
12
53
  declare function goTryRaw<T, E = Error>(promise: Promise<T>): Promise<Result<E, T>>;
13
54
  declare function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
14
55
  declare function goTryRaw<T, E = Error>(value: T): Result<E, T>;
package/dist/index.d.mts CHANGED
@@ -6,9 +6,50 @@ declare function isSuccess<E, T>(result: Result<E, T>): result is Success<T>;
6
6
  declare function isFailure<E, T>(result: Result<E, T>): result is Failure<E>;
7
7
  declare function success<T>(value: T): Success<T>;
8
8
  declare function failure<E>(error: E): Failure<E>;
9
+ /**
10
+ * Executes a function, promise, or value and returns a Result type.
11
+ * If an error occurs, it returns a Failure with the error message as a string.
12
+ *
13
+ * @template T The type of the successful result
14
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
15
+ * @returns {Result<string, T> | Promise<Result<string, T>>} A Result type or a Promise of a Result type
16
+ *
17
+ * @example
18
+ * // With a value
19
+ * const [err, result] = goTry(42);
20
+ *
21
+ * @example
22
+ * // With a function
23
+ * const [err, result] = goTry(() => JSON.parse('{"key": "value"}'));
24
+ *
25
+ * @example
26
+ * // With a promise
27
+ * const [err, result] = await goTry(fetch('https://api.example.com/data'));
28
+ */
9
29
  declare function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>;
10
30
  declare function goTry<T>(fn: () => T): Result<string, T>;
11
31
  declare function goTry<T>(value: T): Result<string, T>;
32
+ /**
33
+ * Executes a function, promise, or value and returns a Result type.
34
+ * If an error occurs, it returns a Failure with the raw error object.
35
+ *
36
+ * @template T The type of the successful result
37
+ * @template E The type of the error, defaults to Error
38
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
39
+ * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
40
+ *
41
+ * @example
42
+ * // With a value
43
+ * const [err, result] = goTryRaw(42);
44
+ *
45
+ * @example
46
+ * // With a function
47
+ * const [err, result] = goTryRaw(() => JSON.parse('{"key": "value"}'));
48
+ *
49
+ * @example
50
+ * // With a promise
51
+ * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
52
+ */
12
53
  declare function goTryRaw<T, E = Error>(promise: Promise<T>): Promise<Result<E, T>>;
13
54
  declare function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
14
55
  declare function goTryRaw<T, E = Error>(value: T): Result<E, T>;
package/dist/index.mjs CHANGED
@@ -11,6 +11,7 @@ function failure(error) {
11
11
  return [error, void 0];
12
12
  }
13
13
  function getErrorMessage(error) {
14
+ if (error === void 0) return "undefined";
14
15
  if (typeof error === "string") return error;
15
16
  if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
16
17
  return error.message;
@@ -24,6 +25,9 @@ function getErrorMessage(error) {
24
25
  function isPromise(value) {
25
26
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
26
27
  }
28
+ function isError(value) {
29
+ return value instanceof Error;
30
+ }
27
31
  function goTry(value) {
28
32
  try {
29
33
  const result = typeof value === "function" ? value() : value;
@@ -39,11 +43,20 @@ function goTryRaw(value) {
39
43
  try {
40
44
  const result = typeof value === "function" ? value() : value;
41
45
  if (isPromise(result)) {
42
- return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(err));
46
+ return result.then((resolvedValue) => success(resolvedValue)).catch((err) => {
47
+ if (err === void 0) {
48
+ return failure(new Error("undefined"));
49
+ }
50
+ return failure(
51
+ isError(err) ? err : new Error(String(err))
52
+ );
53
+ });
43
54
  }
44
55
  return success(result);
45
56
  } catch (err) {
46
- return failure(err);
57
+ return failure(
58
+ isError(err) ? err : new Error(String(err))
59
+ );
47
60
  }
48
61
  }
49
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-go-try",
3
- "version": "6.0.5",
3
+ "version": "6.1.0",
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",
@@ -37,6 +37,9 @@
37
37
  "error handling",
38
38
  "nice-try",
39
39
  "good-try",
40
+ "neverthrow",
41
+ "ts-result",
42
+ "effect",
40
43
  "go-go-try",
41
44
  "gotry",
42
45
  "go-try"
package/src/index.test.ts CHANGED
@@ -3,342 +3,368 @@ import { assert, describe, test } from 'vitest'
3
3
  import { type Result, goTry, goTryRaw, isFailure, isSuccess } from './index.js'
4
4
 
5
5
  test(`value returned by callback is used when callback doesn't throw`, async () => {
6
- const fn = () => 'value'
7
-
8
- // Test with function
9
- const result1 = goTry(fn)
10
- const result2 = goTryRaw(fn)
11
-
12
- // Test destructuring
13
- const [err1, value] = result1
14
- const [err2, value2] = result2
15
-
16
- // Test type guards
17
- assert.equal(isSuccess(result1), true)
18
- assert.equal(isSuccess(result2), true)
19
- assert.equal(isFailure(result1), false)
20
- assert.equal(isFailure(result2), false)
21
-
22
- // Test values
23
- assert.equal(value, 'value')
24
- assert.equal(value2, 'value')
25
- assert.equal(err1, undefined)
26
- assert.equal(err2, undefined)
6
+ const fn = () => 'value'
7
+
8
+ // Test with function
9
+ const result1 = goTry(fn)
10
+ const result2 = goTryRaw(fn)
11
+
12
+ // Test destructuring
13
+ const [err1, value] = result1
14
+ const [err2, value2] = result2
15
+
16
+ // Test type guards
17
+ assert.equal(isSuccess(result1), true)
18
+ assert.equal(isSuccess(result2), true)
19
+ assert.equal(isFailure(result1), false)
20
+ assert.equal(isFailure(result2), false)
21
+
22
+ // Test values
23
+ assert.equal(value, 'value')
24
+ assert.equal(value2, 'value')
25
+ assert.equal(err1, undefined)
26
+ assert.equal(err2, undefined)
27
+ })
28
+
29
+ test('if function throws undefined, err should be "undefined"', async () => {
30
+ const fn = () => {
31
+ throw undefined
32
+ }
33
+
34
+ // Test with function that throws undefined
35
+ const result1 = goTry(fn)
36
+ const result2 = goTryRaw(fn)
37
+
38
+ // Test destructuring
39
+ const [err1, value] = result1
40
+ const [err2, value2] = result2
41
+
42
+ // Test type guards
43
+ assert.equal(isSuccess(result1), false)
44
+ assert.equal(isSuccess(result2), false)
45
+ assert.equal(isFailure(result1), true)
46
+ assert.equal(isFailure(result2), true)
47
+
48
+ // Test values
49
+ assert.equal(value, undefined)
50
+ assert.equal(value2, undefined)
51
+ assert.equal(err1, 'undefined')
52
+ assert.equal(typeof err2, 'object')
27
53
  })
28
54
 
29
55
  test('if callback throws, value should be undefined and err should contain the error message', async () => {
30
- const fn = () => {
31
- return JSON.parse('{/') as string
32
- }
33
-
34
- // Test with function that throws
35
- const result1 = goTry(fn)
36
- const result2 = goTryRaw(fn)
37
-
38
- // Test destructuring
39
- const [err1, value] = result1
40
- const [err2, value2] = result2
41
-
42
- // Test type guards
43
- assert.equal(isSuccess(result1), false)
44
- assert.equal(isSuccess(result2), false)
45
- assert.equal(isFailure(result1), true)
46
- assert.equal(isFailure(result2), true)
47
-
48
- // Test values
49
- assert.equal(value, undefined)
50
- assert.equal(value2, undefined)
51
- assert.equal(typeof err1, 'string')
52
- assert.equal(typeof err2, 'object')
53
- assert.equal(typeof err2?.message, 'string')
56
+ const fn = () => {
57
+ return JSON.parse('{/') as string
58
+ }
59
+
60
+ // Test with function that throws
61
+ const result1 = goTry(fn)
62
+ const result2 = goTryRaw(fn)
63
+
64
+ // Test destructuring
65
+ const [err1, value] = result1
66
+ const [err2, value2] = result2
67
+
68
+ // Test type guards
69
+ assert.equal(isSuccess(result1), false)
70
+ assert.equal(isSuccess(result2), false)
71
+ assert.equal(isFailure(result1), true)
72
+ assert.equal(isFailure(result2), true)
73
+
74
+ // Test values
75
+ assert.equal(value, undefined)
76
+ assert.equal(value2, undefined)
77
+ assert.equal(typeof err1, 'string')
78
+ assert.equal(typeof err2, 'object')
79
+ assert.equal(typeof err2?.message, 'string')
54
80
  })
55
81
 
56
82
  test('first parameter accepts promises and makes the function async', async () => {
57
- const promise = Promise.resolve('value')
58
-
59
- // Test with promise
60
- const result1 = await goTry(promise)
61
- const result2 = await goTryRaw(promise)
62
-
63
- // Test destructuring
64
- const [err1, value] = result1
65
- const [err2, value2] = result2
66
-
67
- // Test type guards
68
- assert.equal(isSuccess(result1), true)
69
- assert.equal(isSuccess(result2), true)
70
- assert.equal(isFailure(result1), false)
71
- assert.equal(isFailure(result2), false)
72
-
73
- // Test values
74
- assert.equal(value, 'value')
75
- assert.equal(value2, 'value')
76
- assert.equal(err1, undefined)
77
- assert.equal(err2, undefined)
83
+ const promise = Promise.resolve('value')
84
+
85
+ // Test with promise
86
+ const result1 = await goTry(promise)
87
+ const result2 = await goTryRaw(promise)
88
+
89
+ // Test destructuring
90
+ const [err1, value] = result1
91
+ const [err2, value2] = result2
92
+
93
+ // Test type guards
94
+ assert.equal(isSuccess(result1), true)
95
+ assert.equal(isSuccess(result2), true)
96
+ assert.equal(isFailure(result1), false)
97
+ assert.equal(isFailure(result2), false)
98
+
99
+ // Test values
100
+ assert.equal(value, 'value')
101
+ assert.equal(value2, 'value')
102
+ assert.equal(err1, undefined)
103
+ assert.equal(err2, undefined)
78
104
  })
79
105
 
80
106
  test('if async callback throws, value should be undefined and err should contain the error message', async () => {
81
- const promise = Promise.reject(new Error('error'))
82
-
83
- // Test with promise that rejects
84
- const result1 = await goTry(promise)
85
- const result2 = await goTryRaw<Error>(promise)
86
-
87
- // Test destructuring
88
- const [err, value] = result1
89
- const [err2, value2] = result2
90
-
91
- // Test type guards
92
- assert.equal(isSuccess(result1), false)
93
- assert.equal(isSuccess(result2), false)
94
- assert.equal(isFailure(result1), true)
95
- assert.equal(isFailure(result2), true)
96
-
97
- // Test values
98
- assert.equal(value, undefined)
99
- assert.equal(value2, undefined)
100
- assert.equal(err, 'error')
101
- assert.equal(typeof err2, 'object')
102
- assert.equal(err2?.message, 'error')
107
+ const promise = Promise.reject(new Error('error'))
108
+
109
+ // Test with promise that rejects
110
+ const result1 = await goTry(promise)
111
+ const result2 = await goTryRaw<Error>(promise)
112
+
113
+ // Test destructuring
114
+ const [err, value] = result1
115
+ const [err2, value2] = result2
116
+
117
+ // Test type guards
118
+ assert.equal(isSuccess(result1), false)
119
+ assert.equal(isSuccess(result2), false)
120
+ assert.equal(isFailure(result1), true)
121
+ assert.equal(isFailure(result2), true)
122
+
123
+ // Test values
124
+ assert.equal(value, undefined)
125
+ assert.equal(value2, undefined)
126
+ assert.equal(err, 'error')
127
+ assert.equal(typeof err2, 'object')
128
+ assert.equal(err2?.message, 'error')
103
129
  })
104
130
 
105
131
  test('direct values work too', () => {
106
- // Test with direct value
107
- const result1 = goTry('value')
108
- const result2 = goTryRaw('value')
109
-
110
- // Test destructuring
111
- const [err1, value] = result1
112
- const [err2, value2] = result2
113
-
114
- // Test values
115
- assert.equal(value, 'value')
116
- assert.equal(value2, 'value')
117
- assert.equal(err1, undefined)
118
- assert.equal(err2, undefined)
132
+ // Test with direct value
133
+ const result1 = goTry('value')
134
+ const result2 = goTryRaw('value')
135
+
136
+ // Test destructuring
137
+ const [err1, value] = result1
138
+ const [err2, value2] = result2
139
+
140
+ // Test values
141
+ assert.equal(value, 'value')
142
+ assert.equal(value2, 'value')
143
+ assert.equal(err1, undefined)
144
+ assert.equal(err2, undefined)
119
145
  })
120
146
 
121
147
  test('async functions work correctly', async () => {
122
- // Define an async function
123
- function asyncFn() {
124
- return Promise.resolve(42)
125
- }
126
-
127
- // Test with async function
128
- const result1 = await goTry(asyncFn())
129
- const result2 = await goTryRaw(asyncFn())
130
-
131
- // Test destructuring
132
- const [err1, value] = result1
133
- const [err2, value2] = result2
134
-
135
- // Test values
136
- assert.equal(value, 42)
137
- assert.equal(value2, 42)
138
- assert.equal(err1, undefined)
139
- assert.equal(err2, undefined)
148
+ // Define an async function
149
+ function asyncFn() {
150
+ return Promise.resolve(42)
151
+ }
152
+
153
+ // Test with async function
154
+ const result1 = await goTry(asyncFn())
155
+ const result2 = await goTryRaw(asyncFn())
156
+
157
+ // Test destructuring
158
+ const [err1, value] = result1
159
+ const [err2, value2] = result2
160
+
161
+ // Test values
162
+ assert.equal(value, 42)
163
+ assert.equal(value2, 42)
164
+ assert.equal(err1, undefined)
165
+ assert.equal(err2, undefined)
140
166
  })
141
167
 
142
168
  test('async functions that throw work correctly', async () => {
143
- // Define an async function that throws
144
- async function asyncFnThatThrows() {
145
- await 1
146
- throw new Error('async error')
147
- }
148
-
149
- // Test with async function that throws
150
- const result1 = await goTry(asyncFnThatThrows())
151
- const result2 = await goTryRaw(asyncFnThatThrows())
152
-
153
- // Test destructuring
154
- const [err1, value] = result1
155
- const [err2, value2] = result2
156
-
157
- // Test values
158
- assert.equal(value, undefined)
159
- assert.equal(value2, undefined)
160
- assert.equal(err1, 'async error')
161
- assert.equal(typeof err2, 'object')
162
- assert.equal(err2?.message, 'async error')
169
+ // Define an async function that throws
170
+ async function asyncFnThatThrows() {
171
+ await 1
172
+ throw new Error('async error')
173
+ }
174
+
175
+ // Test with async function that throws
176
+ const result1 = await goTry(asyncFnThatThrows())
177
+ const result2 = await goTryRaw(asyncFnThatThrows())
178
+
179
+ // Test destructuring
180
+ const [err1, value] = result1
181
+ const [err2, value2] = result2
182
+
183
+ // Test values
184
+ assert.equal(value, undefined)
185
+ assert.equal(value2, undefined)
186
+ assert.equal(err1, 'async error')
187
+ assert.equal(typeof err2, 'object')
188
+ assert.equal(err2?.message, 'async error')
163
189
  })
164
190
 
165
191
  describe('goTry type tests', () => {
166
- test('synchronous function returns correct types', () => {
167
- const fn = () => 'value'
168
- const result = goTry(fn)
169
-
170
- // Check the result type
171
- attest<Result<string, string>>(result)
172
-
173
- // Check the destructured types
174
- const [err, value] = result
175
- attest<string | undefined>(err)
176
- attest<string | undefined>(value)
177
-
178
- // When destructured, the types should be narrowed correctly
179
- if (err === undefined) {
180
- attest<string>(value)
181
- } else {
182
- attest<undefined>(value)
183
- }
184
- })
185
-
186
- test('direct value returns correct types', () => {
187
- const result = goTry('value')
188
-
189
- // Check the result type
190
- attest<Result<string, string>>(result)
191
-
192
- // Check the destructured types
193
- const [err, value] = result
194
- attest<string | undefined>(err)
195
- attest<string | undefined>(value)
196
-
197
- // When destructured, the types should be narrowed correctly
198
- if (err === undefined) {
199
- attest<string>(value)
200
- } else {
201
- attest<undefined>(value)
202
- }
203
- })
204
-
205
- test('promise returns correct types', async () => {
206
- const promise = Promise.resolve('value')
207
- const result = await goTry(promise)
208
-
209
- // Check the result type
210
- attest<Result<string, string>>(result)
211
-
212
- // Check the destructured types
213
- const [err, value] = result
214
- attest<string | undefined>(err)
215
- attest<string | undefined>(value)
216
-
217
- // When destructured, the types should be narrowed correctly
218
- if (err === undefined) {
219
- attest<string>(value)
220
- } else {
221
- attest<undefined>(value)
222
- }
223
- })
224
-
225
- test('async function returns correct types', async () => {
226
- const asyncFn = async () => 'value'
227
- const result = await goTry(asyncFn())
228
-
229
- // Check the result type
230
- attest<Result<string, string>>(result)
231
-
232
- // Check the destructured types
233
- const [err, value] = result
234
- attest<string | undefined>(err)
235
- attest<string | undefined>(value)
236
-
237
- // When destructured, the types should be narrowed correctly
238
- if (err === undefined) {
239
- attest<string>(value)
240
- } else {
241
- attest<undefined>(value)
242
- }
243
- })
192
+ test('synchronous function returns correct types', () => {
193
+ const fn = () => 'value'
194
+ const result = goTry(fn)
195
+
196
+ // Check the result type
197
+ attest<Result<string, string>>(result)
198
+
199
+ // Check the destructured types
200
+ const [err, value] = result
201
+ attest<string | undefined>(err)
202
+ attest<string | undefined>(value)
203
+
204
+ // When destructured, the types should be narrowed correctly
205
+ if (err === undefined) {
206
+ attest<string>(value)
207
+ } else {
208
+ attest<undefined>(value)
209
+ }
210
+ })
211
+
212
+ test('direct value returns correct types', () => {
213
+ const result = goTry('value')
214
+
215
+ // Check the result type
216
+ attest<Result<string, string>>(result)
217
+
218
+ // Check the destructured types
219
+ const [err, value] = result
220
+ attest<string | undefined>(err)
221
+ attest<string | undefined>(value)
222
+
223
+ // When destructured, the types should be narrowed correctly
224
+ if (err === undefined) {
225
+ attest<string>(value)
226
+ } else {
227
+ attest<undefined>(value)
228
+ }
229
+ })
230
+
231
+ test('promise returns correct types', async () => {
232
+ const promise = Promise.resolve('value')
233
+ const result = await goTry(promise)
234
+
235
+ // Check the result type
236
+ attest<Result<string, string>>(result)
237
+
238
+ // Check the destructured types
239
+ const [err, value] = result
240
+ attest<string | undefined>(err)
241
+ attest<string | undefined>(value)
242
+
243
+ // When destructured, the types should be narrowed correctly
244
+ if (err === undefined) {
245
+ attest<string>(value)
246
+ } else {
247
+ attest<undefined>(value)
248
+ }
249
+ })
250
+
251
+ test('async function returns correct types', async () => {
252
+ const asyncFn = async () => 'value'
253
+ const result = await goTry(asyncFn())
254
+
255
+ // Check the result type
256
+ attest<Result<string, string>>(result)
257
+
258
+ // Check the destructured types
259
+ const [err, value] = result
260
+ attest<string | undefined>(err)
261
+ attest<string | undefined>(value)
262
+
263
+ // When destructured, the types should be narrowed correctly
264
+ if (err === undefined) {
265
+ attest<string>(value)
266
+ } else {
267
+ attest<undefined>(value)
268
+ }
269
+ })
244
270
  })
245
271
 
246
272
  describe('goTryRaw type tests', () => {
247
- test('type inference works correctly', () => {
248
- const fn = () => 'value'
249
- const result = goTryRaw(fn)
250
-
251
- // Check the result type
252
- attest<Result<Error, string>>(result)
253
-
254
- // Check the destructured types
255
- const [err, value] = result
256
- attest<Error | undefined>(err)
257
- attest<string | undefined>(value)
258
-
259
- // When destructured, the types should be narrowed correctly
260
- if (err === undefined) {
261
- attest<string>(value)
262
- } else {
263
- attest<undefined>(value)
264
- }
265
- })
266
- test('synchronous function returns correct types', () => {
267
- const fn = () => 'value'
268
- const result = goTryRaw(fn)
269
-
270
- // Check the result type
271
- attest<Result<Error, string>>(result)
272
-
273
- // Check the destructured types
274
- const [err, value] = result
275
- attest<Error | undefined>(err)
276
- attest<string | undefined>(value)
277
-
278
- // When destructured, the types should be narrowed correctly
279
- if (err === undefined) {
280
- attest<string>(value)
281
- } else {
282
- attest<undefined>(value)
283
- }
284
- })
285
-
286
- test('direct value returns correct types', () => {
287
- const result = goTryRaw('value')
288
-
289
- // Check the result type
290
- attest<Result<Error, string>>(result)
291
-
292
- // Check the destructured types
293
- const [err, value] = result
294
- attest<Error | undefined>(err)
295
- attest<string | undefined>(value)
296
-
297
- // When destructured, the types should be narrowed correctly
298
- if (err === undefined) {
299
- attest<string>(value)
300
- } else {
301
- attest<undefined>(value)
302
- }
303
- })
304
-
305
- test('promise returns correct types', async () => {
306
- const promise = Promise.resolve('value')
307
- const result = await goTryRaw(promise)
308
-
309
- // Check the result type
310
- attest<Result<Error, string>>(result)
311
-
312
- // Check the destructured types
313
- const [err, value] = result
314
- attest<Error | undefined>(err)
315
- attest<string | undefined>(value)
316
-
317
- // When destructured, the types should be narrowed correctly
318
- if (err === undefined) {
319
- attest<string>(value)
320
- } else {
321
- attest<undefined>(value)
322
- }
323
- })
324
-
325
- test('async function returns correct types', async () => {
326
- const asyncFn = async () => 'value'
327
- const result = await goTryRaw(asyncFn())
328
-
329
- // Check the result type
330
- attest<Result<Error, string>>(result)
331
-
332
- // Check the destructured types
333
- const [err, value] = result
334
- attest<Error | undefined>(err)
335
- attest<string | undefined>(value)
336
-
337
- // When destructured, the types should be narrowed correctly
338
- if (err === undefined) {
339
- attest<string>(value)
340
- } else {
341
- attest<undefined>(value)
342
- }
343
- })
273
+ test('type inference works correctly', () => {
274
+ const fn = () => 'value'
275
+ const result = goTryRaw(fn)
276
+
277
+ // Check the result type
278
+ attest<Result<Error, string>>(result)
279
+
280
+ // Check the destructured types
281
+ const [err, value] = result
282
+ attest<Error | undefined>(err)
283
+ attest<string | undefined>(value)
284
+
285
+ // When destructured, the types should be narrowed correctly
286
+ if (err === undefined) {
287
+ attest<string>(value)
288
+ } else {
289
+ attest<undefined>(value)
290
+ }
291
+ })
292
+ test('synchronous function returns correct types', () => {
293
+ const fn = () => 'value'
294
+ const result = goTryRaw(fn)
295
+
296
+ // Check the result type
297
+ attest<Result<Error, string>>(result)
298
+
299
+ // Check the destructured types
300
+ const [err, value] = result
301
+ attest<Error | undefined>(err)
302
+ attest<string | undefined>(value)
303
+
304
+ // When destructured, the types should be narrowed correctly
305
+ if (err === undefined) {
306
+ attest<string>(value)
307
+ } else {
308
+ attest<undefined>(value)
309
+ }
310
+ })
311
+
312
+ test('direct value returns correct types', () => {
313
+ const result = goTryRaw('value')
314
+
315
+ // Check the result type
316
+ attest<Result<Error, string>>(result)
317
+
318
+ // Check the destructured types
319
+ const [err, value] = result
320
+ attest<Error | undefined>(err)
321
+ attest<string | undefined>(value)
322
+
323
+ // When destructured, the types should be narrowed correctly
324
+ if (err === undefined) {
325
+ attest<string>(value)
326
+ } else {
327
+ attest<undefined>(value)
328
+ }
329
+ })
330
+
331
+ test('promise returns correct types', async () => {
332
+ const promise = Promise.resolve('value')
333
+ const result = await goTryRaw(promise)
334
+
335
+ // Check the result type
336
+ attest<Result<Error, string>>(result)
337
+
338
+ // Check the destructured types
339
+ const [err, value] = result
340
+ attest<Error | undefined>(err)
341
+ attest<string | undefined>(value)
342
+
343
+ // When destructured, the types should be narrowed correctly
344
+ if (err === undefined) {
345
+ attest<string>(value)
346
+ } else {
347
+ attest<undefined>(value)
348
+ }
349
+ })
350
+
351
+ test('async function returns correct types', async () => {
352
+ const asyncFn = async () => 'value'
353
+ const result = await goTryRaw(asyncFn())
354
+
355
+ // Check the result type
356
+ attest<Result<Error, string>>(result)
357
+
358
+ // Check the destructured types
359
+ const [err, value] = result
360
+ attest<Error | undefined>(err)
361
+ attest<string | undefined>(value)
362
+
363
+ // When destructured, the types should be narrowed correctly
364
+ if (err === undefined) {
365
+ attest<string>(value)
366
+ } else {
367
+ attest<undefined>(value)
368
+ }
369
+ })
344
370
  })
package/src/index.ts CHANGED
@@ -1,92 +1,152 @@
1
- export type Success<T> = readonly [undefined, T];
2
- export type Failure<E> = readonly [E, undefined];
3
- export type Result<E, T> = Success<T> | Failure<E>;
1
+ export type Success<T> = readonly [undefined, T]
2
+ export type Failure<E> = readonly [E, undefined]
3
+ export type Result<E, T> = Success<T> | Failure<E>
4
4
 
5
- export type MaybePromise<T> = T | Promise<T>;
5
+ export type MaybePromise<T> = T | Promise<T>
6
6
 
7
7
  export function isSuccess<E, T>(result: Result<E, T>): result is Success<T> {
8
- return result[0] === undefined;
8
+ return result[0] === undefined
9
9
  }
10
10
  export function isFailure<E, T>(result: Result<E, T>): result is Failure<E> {
11
- return result[0] !== undefined;
11
+ return result[0] !== undefined
12
12
  }
13
13
 
14
14
  export function success<T>(value: T): Success<T> {
15
- return [undefined, value] as const;
15
+ return [undefined, value] as const
16
16
  }
17
17
 
18
18
  export function failure<E>(error: E): Failure<E> {
19
- return [error, undefined] as const;
19
+ return [error, undefined] as const
20
20
  }
21
21
 
22
22
  function getErrorMessage(error: unknown): string {
23
- if (typeof error === "string") return error;
23
+ if (error === undefined) return 'undefined'
24
+
25
+ if (typeof error === 'string') return error
24
26
 
25
27
  if (
26
- typeof error === "object" &&
28
+ typeof error === 'object' &&
27
29
  error !== null &&
28
- "message" in error &&
29
- typeof (error as Record<string, unknown>).message === "string"
30
+ 'message' in error &&
31
+ typeof (error as Record<string, unknown>).message === 'string'
30
32
  ) {
31
- return (error as { message: string }).message;
33
+ return (error as { message: string }).message
32
34
  }
33
35
 
34
36
  try {
35
- return JSON.stringify(error);
37
+ return JSON.stringify(error)
36
38
  } catch {
37
39
  // fallback in case there's an error stringifying the error
38
40
  // with circular references for example.
39
- return String(error);
41
+ return String(error)
40
42
  }
41
43
  }
42
44
 
43
45
  function isPromise<T>(value: unknown): value is Promise<T> {
44
46
  return (
45
- typeof value === "object" &&
47
+ typeof value === 'object' &&
46
48
  value !== null &&
47
- "then" in value &&
48
- typeof (value as { then: unknown }).then === "function"
49
- );
49
+ 'then' in value &&
50
+ typeof (value as { then: unknown }).then === 'function'
51
+ )
52
+ }
53
+
54
+ function isError(value: unknown): value is Error {
55
+ return value instanceof Error
50
56
  }
51
57
 
52
- export function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>;
53
- export function goTry<T>(fn: () => T): Result<string, T>;
54
- export function goTry<T>(value: T): Result<string, T>;
58
+ /**
59
+ * Executes a function, promise, or value and returns a Result type.
60
+ * If an error occurs, it returns a Failure with the error message as a string.
61
+ *
62
+ * @template T The type of the successful result
63
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
64
+ * @returns {Result<string, T> | Promise<Result<string, T>>} A Result type or a Promise of a Result type
65
+ *
66
+ * @example
67
+ * // With a value
68
+ * const [err, result] = goTry(42);
69
+ *
70
+ * @example
71
+ * // With a function
72
+ * const [err, result] = goTry(() => JSON.parse('{"key": "value"}'));
73
+ *
74
+ * @example
75
+ * // With a promise
76
+ * const [err, result] = await goTry(fetch('https://api.example.com/data'));
77
+ */
78
+ export function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>
79
+ export function goTry<T>(fn: () => T): Result<string, T>
80
+ export function goTry<T>(value: T): Result<string, T>
55
81
  export function goTry<T>(
56
82
  value: T | Promise<T> | (() => T | Promise<T>),
57
83
  ): Result<string, T> | Promise<Result<string, T>> {
58
84
  try {
59
85
  const result =
60
- typeof value === "function" ? (value as () => T | Promise<T>)() : value;
86
+ typeof value === 'function' ? (value as () => T | Promise<T>)() : value
61
87
  if (isPromise<T>(result)) {
62
88
  return result
63
89
  .then((resolvedValue) => success<T>(resolvedValue))
64
- .catch((err) => failure<string>(getErrorMessage(err)));
90
+ .catch((err) => failure<string>(getErrorMessage(err)))
65
91
  }
66
- return success<T>(result);
92
+ return success<T>(result)
67
93
  } catch (err) {
68
- return failure<string>(getErrorMessage(err));
94
+ return failure<string>(getErrorMessage(err))
69
95
  }
70
96
  }
71
97
 
98
+ /**
99
+ * Executes a function, promise, or value and returns a Result type.
100
+ * If an error occurs, it returns a Failure with the raw error object.
101
+ *
102
+ * @template T The type of the successful result
103
+ * @template E The type of the error, defaults to Error
104
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
105
+ * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
106
+ *
107
+ * @example
108
+ * // With a value
109
+ * const [err, result] = goTryRaw(42);
110
+ *
111
+ * @example
112
+ * // With a function
113
+ * const [err, result] = goTryRaw(() => JSON.parse('{"key": "value"}'));
114
+ *
115
+ * @example
116
+ * // With a promise
117
+ * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
118
+ */
72
119
  export function goTryRaw<T, E = Error>(
73
120
  promise: Promise<T>,
74
- ): Promise<Result<E, T>>;
75
- export function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
76
- export function goTryRaw<T, E = Error>(value: T): Result<E, T>;
121
+ ): Promise<Result<E, T>>
122
+ export function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>
123
+ export function goTryRaw<T, E = Error>(value: T): Result<E, T>
77
124
  export function goTryRaw<T, E = Error>(
78
125
  value: T | Promise<T> | (() => T | Promise<T>),
79
126
  ): Result<E, T> | Promise<Result<E, T>> {
80
127
  try {
81
128
  const result =
82
- typeof value === "function" ? (value as () => T | Promise<T>)() : value;
129
+ typeof value === 'function' ? (value as () => T | Promise<T>)() : value
83
130
  if (isPromise<T>(result)) {
84
131
  return result
85
132
  .then((resolvedValue) => success<T>(resolvedValue))
86
- .catch((err) => failure<E>(err as E));
133
+ .catch((err) => {
134
+ if (err === undefined) {
135
+ return failure<E>(new Error('undefined') as unknown as E)
136
+ }
137
+ return failure<E>(
138
+ isError(err)
139
+ ? (err as unknown as E)
140
+ : (new Error(String(err)) as unknown as E),
141
+ )
142
+ })
87
143
  }
88
- return success<T>(result);
144
+ return success<T>(result)
89
145
  } catch (err) {
90
- return failure<E>(err as E);
146
+ return failure<E>(
147
+ isError(err)
148
+ ? (err as unknown as E)
149
+ : (new Error(String(err)) as unknown as E),
150
+ )
91
151
  }
92
152
  }