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 +15 -2
- package/dist/index.mjs +15 -2
- package/package.json +53 -53
- package/src/index.test.ts +340 -314
- package/src/index.ts +72 -53
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) =>
|
|
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(
|
|
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) =>
|
|
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(
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
8
|
+
return result[0] === undefined
|
|
9
9
|
}
|
|
10
10
|
export function isFailure<E, T>(result: Result<E, T>): result is Failure<E> {
|
|
11
|
-
|
|
11
|
+
return result[0] !== undefined
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function success<T>(value: T): Success<T> {
|
|
15
|
-
|
|
15
|
+
return [undefined, value] as const
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function failure<E>(error: E): Failure<E> {
|
|
19
|
-
|
|
19
|
+
return [error, undefined] as const
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function getErrorMessage(error: unknown): string {
|
|
23
|
-
|
|
23
|
+
if (error === undefined) return 'undefined'
|
|
24
24
|
|
|
25
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
82
|
+
value: T | Promise<T> | (() => T | Promise<T>),
|
|
77
83
|
): Result<string, T> | Promise<Result<string, T>> {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
+
value: T | Promise<T> | (() => T | Promise<T>),
|
|
120
126
|
): Result<E, T> | Promise<Result<E, T>> {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
}
|