@mkvlrn/result 5.0.8 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -27
- package/build/index.d.ts +14 -7
- package/build/index.js +38 -16
- package/build/index.js.map +1 -1
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Dead simple Result pattern for TypeScript.
|
|
4
4
|
|
|
5
|
-
No `.map()`, no `.flatMap()`, no `.andThen()`, no `.orElse()`, no `.unwrap()`, no monadic gymnastics. Just
|
|
5
|
+
No `.map()`, no `.flatMap()`, no `.andThen()`, no `.orElse()`, no `.unwrap()`, no monadic gymnastics. Just two types with two functions. Then TypeScript does its thing.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@mkvlrn/result)
|
|
8
8
|
|
|
@@ -10,9 +10,9 @@ No `.map()`, no `.flatMap()`, no `.andThen()`, no `.orElse()`, no `.unwrap()`, n
|
|
|
10
10
|
|
|
11
11
|
There are dozens of Result libraries for TypeScript. Nearly all of them bolt on method chaining, transformation pipelines, and functional programming utilities that turn a simple concept into an entire paradigm.
|
|
12
12
|
|
|
13
|
-
This package does **one thing**: gives you a type-safe `
|
|
13
|
+
This package does **one thing**: gives you a type-safe `ResultSync<T, E>` or `ResultAsync<T, E>` discriminated union with `ok()` and `err()` constructors. You use `if/else` to handle it. TypeScript narrows the type for you. That's the whole API.
|
|
14
14
|
|
|
15
|
-
**The entire implementation is ~35 lines. Zero runtime dependencies.
|
|
15
|
+
**The entire implementation is ~35 lines. Zero runtime dependencies. Two exports.**
|
|
16
16
|
|
|
17
17
|
If you need `.map().flatMap().andThen().orElse().unwrapOr()` chains, use [neverthrow](https://github.com/supermacro/neverthrow) or [ts-results](https://github.com/vultix/ts-results). They're good libraries. This isn't that.
|
|
18
18
|
|
|
@@ -24,61 +24,59 @@ pnpm add @mkvlrn/result
|
|
|
24
24
|
|
|
25
25
|
## API
|
|
26
26
|
|
|
27
|
-
| Export | What it does
|
|
28
|
-
| ------------------- |
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `ok(value)` | Creates a success result |
|
|
32
|
-
| `err(error)` | Creates an error result |
|
|
27
|
+
| Export | What it does |
|
|
28
|
+
| ------------------- | ------------------------------------------- |
|
|
29
|
+
| `ResultSync<T, E>` | Sync Result type and `{ ok, err }` helpers |
|
|
30
|
+
| `ResultAsync<T, E>` | Async Result type and `{ ok, err }` helpers |
|
|
33
31
|
|
|
34
32
|
That's it. That's the whole thing.
|
|
35
33
|
|
|
36
34
|
## Usage
|
|
37
35
|
|
|
38
36
|
```typescript
|
|
39
|
-
import {
|
|
37
|
+
import { ResultSync, ResultAsync } from "@mkvlrn/result";
|
|
40
38
|
```
|
|
41
39
|
|
|
42
40
|
### Create results, check results
|
|
43
41
|
|
|
44
42
|
```typescript
|
|
45
|
-
function divide(a: number, b: number):
|
|
43
|
+
function divide(a: number, b: number): ResultSync<number, Error> {
|
|
46
44
|
if (b === 0) {
|
|
47
|
-
return err(new Error("Division by zero"));
|
|
45
|
+
return ResultSync.err(new Error("Division by zero"));
|
|
48
46
|
}
|
|
49
|
-
return ok(a / b);
|
|
50
|
-
}
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
if (result.isOk) {
|
|
54
|
-
console.log(result.value); // number - TypeScript knows
|
|
48
|
+
return ResultSync.ok(a / b);
|
|
55
49
|
}
|
|
56
50
|
|
|
51
|
+
const result = divide(10, 2);
|
|
57
52
|
if (result.isError) {
|
|
58
53
|
console.log(result.error.message); // Error - TypeScript knows
|
|
54
|
+
} else {
|
|
55
|
+
console.log(result.value); // number - TypeScript knows
|
|
59
56
|
}
|
|
60
57
|
```
|
|
61
58
|
|
|
62
|
-
No `.unwrap()
|
|
59
|
+
No `.unwrap()`, no `.expect()`, no other dozens of functions. You just use an `if` statement and the compiler handles the rest.
|
|
63
60
|
|
|
64
61
|
### Async Operations
|
|
65
62
|
|
|
66
63
|
```typescript
|
|
67
|
-
async function fetchUser(id: number):
|
|
64
|
+
async function fetchUser(id: number): ResultAsync<User, Error> {
|
|
68
65
|
try {
|
|
69
66
|
const response = await fetch(`/api/users/${id}`);
|
|
70
67
|
if (!response.ok) {
|
|
71
|
-
return err(new Error(`HTTP ${response.status}`));
|
|
68
|
+
return ResultAsync.err(new Error(`HTTP ${response.status}`));
|
|
72
69
|
}
|
|
73
70
|
const user = await response.json();
|
|
74
|
-
|
|
71
|
+
|
|
72
|
+
return ResultAsync.ok(user);
|
|
75
73
|
} catch (error) {
|
|
76
|
-
return err(error instanceof Error ? error : new Error("Unknown error"));
|
|
74
|
+
return ResultAsync.err(error instanceof Error ? error : new Error("Unknown error"));
|
|
77
75
|
}
|
|
78
76
|
}
|
|
79
77
|
```
|
|
80
78
|
|
|
81
|
-
`
|
|
79
|
+
`ResultAsync<T, E>` is just `Promise<ResultSync<T, E>>`. It's a type alias.
|
|
82
80
|
|
|
83
81
|
### Custom Error Types
|
|
84
82
|
|
|
@@ -93,17 +91,18 @@ class ValidationError extends Error {
|
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
function validateEmail(email: string):
|
|
94
|
+
function validateEmail(email: string): ResultSync<string, ValidationError> {
|
|
97
95
|
if (!email.includes("@")) {
|
|
98
|
-
return err(new ValidationError(400, "bad-email"));
|
|
96
|
+
return ResultSync.err(new ValidationError(400, "bad-email"));
|
|
99
97
|
}
|
|
100
|
-
|
|
98
|
+
|
|
99
|
+
return ResultSync.ok(email);
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
const result = validateEmail("invalid-email");
|
|
104
103
|
if (result.isError) {
|
|
105
104
|
// TypeScript knows this is a ValidationError, not just Error
|
|
106
|
-
console.log(`${result.error.code}
|
|
105
|
+
console.log(`${result.error.code}:${result.error.message}`); // 400: bad-email
|
|
107
106
|
}
|
|
108
107
|
```
|
|
109
108
|
|
package/build/index.d.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
//#region src/index.d.ts
|
|
2
|
-
type
|
|
2
|
+
type SyncResult<T, E extends Error> = {
|
|
3
3
|
readonly isError: false;
|
|
4
|
-
readonly isOk: true;
|
|
5
4
|
readonly value: T;
|
|
6
5
|
} | {
|
|
7
6
|
readonly isError: true;
|
|
8
|
-
readonly isOk: false;
|
|
9
7
|
readonly error: E;
|
|
10
8
|
};
|
|
11
|
-
|
|
12
|
-
declare
|
|
13
|
-
|
|
9
|
+
declare const ok: <T>(value: T) => SyncResult<T, never>;
|
|
10
|
+
declare const err: <E extends Error>(error: E) => SyncResult<never, E>;
|
|
11
|
+
type ResultSync<T, E extends Error> = SyncResult<T, E>;
|
|
12
|
+
declare const ResultSync: {
|
|
13
|
+
ok: typeof ok;
|
|
14
|
+
err: typeof err;
|
|
15
|
+
};
|
|
16
|
+
type ResultAsync<T, E extends Error> = Promise<SyncResult<T, E>>;
|
|
17
|
+
declare const ResultAsync: {
|
|
18
|
+
ok: typeof ok;
|
|
19
|
+
err: typeof err;
|
|
20
|
+
};
|
|
14
21
|
//#endregion
|
|
15
|
-
export {
|
|
22
|
+
export { ResultAsync, ResultSync };
|
|
16
23
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.js
CHANGED
|
@@ -4,26 +4,48 @@
|
|
|
4
4
|
* @param value The success value
|
|
5
5
|
* @returns A Result object representing success
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
value
|
|
12
|
-
};
|
|
13
|
-
}
|
|
7
|
+
const ok = (value) => ({
|
|
8
|
+
isError: false,
|
|
9
|
+
value
|
|
10
|
+
});
|
|
14
11
|
/**
|
|
15
12
|
* Creates an error Result with the given error.
|
|
16
13
|
* @param error The error value
|
|
17
14
|
* @returns A Result object representing error
|
|
18
15
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
const err = (error) => ({
|
|
17
|
+
isError: true,
|
|
18
|
+
error
|
|
19
|
+
});
|
|
20
|
+
const ResultSync = {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a successful Result with the given value.
|
|
23
|
+
* @param value The success value
|
|
24
|
+
* @returns A Result object representing success
|
|
25
|
+
*/
|
|
26
|
+
ok,
|
|
27
|
+
/**
|
|
28
|
+
* Creates an error Result with the given error.
|
|
29
|
+
* @param error The error value
|
|
30
|
+
* @returns A Result object representing error
|
|
31
|
+
*/
|
|
32
|
+
err
|
|
33
|
+
};
|
|
34
|
+
const ResultAsync = {
|
|
35
|
+
/**
|
|
36
|
+
* Creates a successful Result with the given value.
|
|
37
|
+
* @param value The success value
|
|
38
|
+
* @returns A Result object representing success
|
|
39
|
+
*/
|
|
40
|
+
ok,
|
|
41
|
+
/**
|
|
42
|
+
* Creates an error Result with the given error.
|
|
43
|
+
* @param error The error value
|
|
44
|
+
* @returns A Result object representing error
|
|
45
|
+
*/
|
|
46
|
+
err
|
|
47
|
+
};
|
|
27
48
|
//#endregion
|
|
28
|
-
export {
|
|
49
|
+
export { ResultAsync, ResultSync };
|
|
50
|
+
|
|
29
51
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["type SyncResult<T, E extends Error> =\n | { readonly isError: false; readonly value: T }\n | { readonly isError: true; readonly error: E };\n\n/**\n * Creates a successful Result with the given value.\n * @param value The success value\n * @returns A Result object representing success\n */\nconst ok = <T>(value: T): SyncResult<T, never> => ({\n isError: false,\n value,\n});\n\n/**\n * Creates an error Result with the given error.\n * @param error The error value\n * @returns A Result object representing error\n */\nconst err = <E extends Error>(error: E): SyncResult<never, E> => ({\n isError: true,\n error,\n});\n\n/**\n * Result type to represent the synchronous outcome of an operation.\n * It can either be a success with a value or an error.\n *\n * It is also an object containing the ok and err functions to\n * make it easier to create Result objects.\n */\nexport type ResultSync<T, E extends Error> = SyncResult<T, E>;\n\nexport const ResultSync = {\n /**\n * Creates a successful Result with the given value.\n * @param value The success value\n * @returns A Result object representing success\n */\n ok,\n /**\n * Creates an error Result with the given error.\n * @param error The error value\n * @returns A Result object representing error\n */\n err,\n};\n\n/**\n * Async version of Result type that wraps a Result in a Promise.\n *\n * It is also an object containing the ok and err functions to\n * make it easier to create Result objects for async workflows.\n */\nexport type ResultAsync<T, E extends Error> = Promise<SyncResult<T, E>>;\n\nexport const ResultAsync = {\n /**\n * Creates a successful Result with the given value.\n * @param value The success value\n * @returns A Result object representing success\n */\n ok,\n /**\n * Creates an error Result with the given error.\n * @param error The error value\n * @returns A Result object representing error\n */\n err,\n};\n"],"mappings":";;;;;;AASA,MAAM,MAAS,WAAoC;CACjD,SAAS;CACT;AACF;;;;;;AAOA,MAAM,OAAwB,WAAoC;CAChE,SAAS;CACT;AACF;AAWA,MAAa,aAAa;;;;;;CAMxB;;;;;;CAMA;AACF;AAUA,MAAa,cAAc;;;;;;CAMzB;;;;;;CAMA;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mkvlrn/result",
|
|
3
3
|
"description": "Simple Result type/pattern for TypeScript",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Mike Valeriano <mkvlrn@gmail.com>",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git@github.com:mkvlrn/tools"
|
|
11
11
|
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"registry": "https://registry.npmjs.org/"
|
|
15
|
+
},
|
|
12
16
|
"keywords": [
|
|
13
17
|
"node",
|
|
14
18
|
"typescript",
|
|
@@ -25,15 +29,13 @@
|
|
|
25
29
|
"default": "./build/index.js"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
|
28
|
-
"@types/node": "^
|
|
29
|
-
"lint-staged": "^16.3.2",
|
|
32
|
+
"@types/node": "^26.1.1",
|
|
30
33
|
"tsc-files": "^1.1.4",
|
|
31
|
-
"tsdown": "0.
|
|
32
|
-
"typescript": "
|
|
33
|
-
"vitest": "^4.
|
|
34
|
+
"tsdown": "0.22.14",
|
|
35
|
+
"typescript": "^7.0.2",
|
|
36
|
+
"vitest": "^4.1.10"
|
|
34
37
|
},
|
|
35
38
|
"scripts": {
|
|
36
|
-
"build": "tsdown"
|
|
37
|
-
"typecheck": "tsc --noEmit"
|
|
39
|
+
"build": "tsdown"
|
|
38
40
|
}
|
|
39
41
|
}
|