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 +2 -6
- package/dist/index.cjs +15 -2
- package/dist/index.d.cts +41 -0
- package/dist/index.d.mts +41 -0
- package/dist/index.mjs +15 -2
- package/package.json +4 -1
- package/src/index.test.ts +340 -314
- package/src/index.ts +93 -33
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
|
|
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) =>
|
|
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.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) =>
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "go-go-try",
|
|
3
|
-
"version": "6.0
|
|
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
|
-
|
|
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
|
@@ -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 (
|
|
23
|
+
if (error === undefined) return 'undefined'
|
|
24
|
+
|
|
25
|
+
if (typeof error === 'string') return error
|
|
24
26
|
|
|
25
27
|
if (
|
|
26
|
-
typeof error ===
|
|
28
|
+
typeof error === 'object' &&
|
|
27
29
|
error !== null &&
|
|
28
|
-
|
|
29
|
-
typeof (error as Record<string, unknown>).message ===
|
|
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 ===
|
|
47
|
+
typeof value === 'object' &&
|
|
46
48
|
value !== null &&
|
|
47
|
-
|
|
48
|
-
typeof (value as { then: unknown }).then ===
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 ===
|
|
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 ===
|
|
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) =>
|
|
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>(
|
|
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
|
}
|