go-go-try 6.0.6 → 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/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.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,55 +1,55 @@
1
1
  {
2
- "name": "go-go-try",
3
- "version": "6.0.6",
4
- "description": "Tries to execute a sync/async function, returns a result tuple",
5
- "license": "MIT",
6
- "repository": "thelinuxlich/go-go-try",
7
- "author": {
8
- "name": "Alisson Cavalcante Agiani",
9
- "email": "thelinuxlich@gmail.com"
10
- },
11
- "type": "module",
12
- "main": "./dist/index.cjs",
13
- "module": "./dist/index.mjs",
14
- "types": "./dist/index.d.mts",
15
- "exports": {
16
- "require": {
17
- "types": "./dist/index.d.cts",
18
- "default": "./dist/index.cjs"
19
- },
20
- "import": {
21
- "types": "./dist/index.d.mts",
22
- "default": "./dist/index.mjs"
23
- }
24
- },
25
- "engines": {
26
- "node": ">=16"
27
- },
28
- "scripts": {
29
- "build": "pkgroll",
30
- "lint": "biome lint --write src/*.ts",
31
- "test": "npm run build && npm run lint && vitest run"
32
- },
33
- "keywords": [
34
- "errors",
35
- "try",
36
- "catch",
37
- "error handling",
38
- "nice-try",
39
- "good-try",
40
- "neverthrow",
41
- "ts-result",
42
- "effect",
43
- "go-go-try",
44
- "gotry",
45
- "go-try"
46
- ],
47
- "devDependencies": {
48
- "@ark/attest": "^0.46.0",
49
- "@biomejs/biome": "^1.9.4",
50
- "pkgroll": "^2.12.1",
51
- "tsx": "^4.19.3",
52
- "typescript": "^5.8.3",
53
- "vitest": "^3.1.2"
54
- }
2
+ "name": "go-go-try",
3
+ "version": "6.1.0",
4
+ "description": "Tries to execute a sync/async function, returns a result tuple",
5
+ "license": "MIT",
6
+ "repository": "thelinuxlich/go-go-try",
7
+ "author": {
8
+ "name": "Alisson Cavalcante Agiani",
9
+ "email": "thelinuxlich@gmail.com"
10
+ },
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.mts",
15
+ "exports": {
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ },
20
+ "import": {
21
+ "types": "./dist/index.d.mts",
22
+ "default": "./dist/index.mjs"
23
+ }
24
+ },
25
+ "engines": {
26
+ "node": ">=16"
27
+ },
28
+ "scripts": {
29
+ "build": "pkgroll",
30
+ "lint": "biome lint --write src/*.ts",
31
+ "test": "npm run build && npm run lint && vitest run"
32
+ },
33
+ "keywords": [
34
+ "errors",
35
+ "try",
36
+ "catch",
37
+ "error handling",
38
+ "nice-try",
39
+ "good-try",
40
+ "neverthrow",
41
+ "ts-result",
42
+ "effect",
43
+ "go-go-try",
44
+ "gotry",
45
+ "go-try"
46
+ ],
47
+ "devDependencies": {
48
+ "@ark/attest": "^0.46.0",
49
+ "@biomejs/biome": "^1.9.4",
50
+ "pkgroll": "^2.12.1",
51
+ "tsx": "^4.19.3",
52
+ "typescript": "^5.8.3",
53
+ "vitest": "^3.1.2"
54
+ }
55
55
  }
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
@@ -5,48 +5,54 @@ export type Result<E, T> = Success<T> | Failure<E>
5
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
24
 
25
- if (
26
- typeof error === 'object' &&
27
- error !== null &&
28
- 'message' in error &&
29
- typeof (error as Record<string, unknown>).message === 'string'
30
- ) {
31
- return (error as { message: string }).message
32
- }
25
+ if (typeof error === 'string') return error
33
26
 
34
- try {
35
- return JSON.stringify(error)
36
- } catch {
37
- // fallback in case there's an error stringifying the error
38
- // with circular references for example.
39
- return String(error)
40
- }
27
+ if (
28
+ typeof error === 'object' &&
29
+ error !== null &&
30
+ 'message' in error &&
31
+ typeof (error as Record<string, unknown>).message === 'string'
32
+ ) {
33
+ return (error as { message: string }).message
34
+ }
35
+
36
+ try {
37
+ return JSON.stringify(error)
38
+ } catch {
39
+ // fallback in case there's an error stringifying the error
40
+ // with circular references for example.
41
+ return String(error)
42
+ }
41
43
  }
42
44
 
43
45
  function isPromise<T>(value: unknown): value is Promise<T> {
44
- return (
45
- typeof value === 'object' &&
46
- value !== null &&
47
- 'then' in value &&
48
- typeof (value as { then: unknown }).then === 'function'
49
- )
46
+ return (
47
+ typeof value === 'object' &&
48
+ value !== null &&
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
58
  /**
@@ -73,20 +79,20 @@ export function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>
73
79
  export function goTry<T>(fn: () => T): Result<string, T>
74
80
  export function goTry<T>(value: T): Result<string, T>
75
81
  export function goTry<T>(
76
- value: T | Promise<T> | (() => T | Promise<T>),
82
+ value: T | Promise<T> | (() => T | Promise<T>),
77
83
  ): Result<string, T> | Promise<Result<string, T>> {
78
- try {
79
- const result =
80
- typeof value === 'function' ? (value as () => T | Promise<T>)() : value
81
- if (isPromise<T>(result)) {
82
- return result
83
- .then((resolvedValue) => success<T>(resolvedValue))
84
- .catch((err) => failure<string>(getErrorMessage(err)))
85
- }
86
- return success<T>(result)
87
- } catch (err) {
88
- return failure<string>(getErrorMessage(err))
89
- }
84
+ try {
85
+ const result =
86
+ typeof value === 'function' ? (value as () => T | Promise<T>)() : value
87
+ if (isPromise<T>(result)) {
88
+ return result
89
+ .then((resolvedValue) => success<T>(resolvedValue))
90
+ .catch((err) => failure<string>(getErrorMessage(err)))
91
+ }
92
+ return success<T>(result)
93
+ } catch (err) {
94
+ return failure<string>(getErrorMessage(err))
95
+ }
90
96
  }
91
97
 
92
98
  /**
@@ -111,23 +117,36 @@ export function goTry<T>(
111
117
  * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
112
118
  */
113
119
  export function goTryRaw<T, E = Error>(
114
- promise: Promise<T>,
120
+ promise: Promise<T>,
115
121
  ): Promise<Result<E, T>>
116
122
  export function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>
117
123
  export function goTryRaw<T, E = Error>(value: T): Result<E, T>
118
124
  export function goTryRaw<T, E = Error>(
119
- value: T | Promise<T> | (() => T | Promise<T>),
125
+ value: T | Promise<T> | (() => T | Promise<T>),
120
126
  ): Result<E, T> | Promise<Result<E, T>> {
121
- try {
122
- const result =
123
- typeof value === 'function' ? (value as () => T | Promise<T>)() : value
124
- if (isPromise<T>(result)) {
125
- return result
126
- .then((resolvedValue) => success<T>(resolvedValue))
127
- .catch((err) => failure<E>(err as E))
128
- }
129
- return success<T>(result)
130
- } catch (err) {
131
- return failure<E>(err as E)
132
- }
127
+ try {
128
+ const result =
129
+ typeof value === 'function' ? (value as () => T | Promise<T>)() : value
130
+ if (isPromise<T>(result)) {
131
+ return result
132
+ .then((resolvedValue) => success<T>(resolvedValue))
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
+ })
143
+ }
144
+ return success<T>(result)
145
+ } catch (err) {
146
+ return failure<E>(
147
+ isError(err)
148
+ ? (err as unknown as E)
149
+ : (new Error(String(err)) as unknown as E),
150
+ )
151
+ }
133
152
  }