@mkvlrn/result 5.0.7 → 6.0.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 +49 -43
- package/package.json +9 -10
- package/build/index.d.ts +0 -16
- package/build/index.js +0 -29
- package/build/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,102 +1,108 @@
|
|
|
1
1
|
# @mkvlrn/result
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Dead simple Result pattern for TypeScript.
|
|
4
|
+
|
|
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.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/@mkvlrn/result)
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Why this one?
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
pnpm add @mkvlrn/result
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
import { Result, AsyncResult, ok, err } from "@mkvlrn/result";
|
|
15
|
+
**The entire implementation is ~35 lines. Zero runtime dependencies. Two exports.**
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
const success = ok(42);
|
|
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.
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
const failure = err(new Error("Something went wrong"));
|
|
19
|
+
## Installation
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (result.isError) {
|
|
27
|
-
console.log("Error:", result.error.message);
|
|
28
|
-
} else {
|
|
29
|
-
console.log("Value:", result.value);
|
|
30
|
-
}
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @mkvlrn/result
|
|
31
23
|
```
|
|
32
24
|
|
|
33
25
|
## API
|
|
34
26
|
|
|
35
|
-
| Export |
|
|
36
|
-
| ------------------- |
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `ok(value)` | Creates a success result |
|
|
40
|
-
| `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 |
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
That's it. That's the whole thing.
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { ResultSync, ResultAsync } from "@mkvlrn/result";
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Create results, check results
|
|
45
41
|
|
|
46
42
|
```typescript
|
|
47
|
-
function divide(a: number, b: number):
|
|
43
|
+
function divide(a: number, b: number): ResultSync<number, Error> {
|
|
48
44
|
if (b === 0) {
|
|
49
|
-
return err(new Error("Division by zero"));
|
|
45
|
+
return ResultSync.err(new Error("Division by zero"));
|
|
50
46
|
}
|
|
51
|
-
|
|
47
|
+
|
|
48
|
+
return ResultSync.ok(a / b);
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
const result = divide(10, 2);
|
|
55
|
-
if (result.
|
|
56
|
-
console.log(result.
|
|
52
|
+
if (result.isError) {
|
|
53
|
+
console.log(result.error.message); // Error - TypeScript knows
|
|
54
|
+
} else {
|
|
55
|
+
console.log(result.value); // number - TypeScript knows
|
|
57
56
|
}
|
|
58
57
|
```
|
|
59
58
|
|
|
59
|
+
No `.unwrap()`, no `.expect()`, no other dozens of functions. You just use an `if` statement and the compiler handles the rest.
|
|
60
|
+
|
|
60
61
|
### Async Operations
|
|
61
62
|
|
|
62
63
|
```typescript
|
|
63
|
-
async function fetchUser(id: number):
|
|
64
|
+
async function fetchUser(id: number): ResultAsync<User, Error> {
|
|
64
65
|
try {
|
|
65
66
|
const response = await fetch(`/api/users/${id}`);
|
|
66
67
|
if (!response.ok) {
|
|
67
|
-
return err(new Error(`HTTP ${response.status}`));
|
|
68
|
+
return ResultAsync.err(new Error(`HTTP ${response.status}`));
|
|
68
69
|
}
|
|
69
70
|
const user = await response.json();
|
|
70
|
-
|
|
71
|
+
|
|
72
|
+
return ResultAsync.ok(user);
|
|
71
73
|
} catch (error) {
|
|
72
|
-
return err(error instanceof Error ? error : new Error("Unknown error"));
|
|
74
|
+
return ResultAsync.err(error instanceof Error ? error : new Error("Unknown error"));
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
```
|
|
76
78
|
|
|
79
|
+
`ResultAsync<T, E>` is just `Promise<ResultSync<T, E>>`. It's a type alias.
|
|
80
|
+
|
|
77
81
|
### Custom Error Types
|
|
78
82
|
|
|
79
83
|
```typescript
|
|
80
84
|
class ValidationError extends Error {
|
|
81
|
-
readonly
|
|
85
|
+
readonly code: number;
|
|
82
86
|
|
|
83
|
-
constructor(
|
|
87
|
+
constructor(code: number, message: string) {
|
|
84
88
|
super(message);
|
|
85
89
|
this.name = "ValidationError";
|
|
86
|
-
this.
|
|
90
|
+
this.code = code;
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
function validateEmail(email: string):
|
|
94
|
+
function validateEmail(email: string): ResultSync<string, ValidationError> {
|
|
91
95
|
if (!email.includes("@")) {
|
|
92
|
-
return err(new ValidationError(400, "
|
|
96
|
+
return ResultSync.err(new ValidationError(400, "bad-email"));
|
|
93
97
|
}
|
|
94
|
-
|
|
98
|
+
|
|
99
|
+
return ResultSync.ok(email);
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
const result = validateEmail("invalid-email");
|
|
98
103
|
if (result.isError) {
|
|
99
|
-
|
|
104
|
+
// TypeScript knows this is a ValidationError, not just Error
|
|
105
|
+
console.log(`${result.error.code}:${result.error.message}`); // 400: bad-email
|
|
100
106
|
}
|
|
101
107
|
```
|
|
102
108
|
|
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.0",
|
|
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,10 @@
|
|
|
25
29
|
"default": "./build/index.js"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
|
28
|
-
"@types/node": "^
|
|
29
|
-
"lint-staged": "^16.2.7",
|
|
32
|
+
"@types/node": "^26.1.1",
|
|
30
33
|
"tsc-files": "^1.1.4",
|
|
31
|
-
"tsdown": "0.
|
|
32
|
-
"typescript": "
|
|
33
|
-
"vitest": "^4.
|
|
34
|
-
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"build": "tsdown",
|
|
37
|
-
"typecheck": "tsc --noEmit"
|
|
34
|
+
"tsdown": "0.22.14",
|
|
35
|
+
"typescript": "^7.0.2",
|
|
36
|
+
"vitest": "^4.1.10"
|
|
38
37
|
}
|
|
39
38
|
}
|
package/build/index.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
//#region src/index.d.ts
|
|
2
|
-
type Result<T, E extends Error> = {
|
|
3
|
-
readonly isError: false;
|
|
4
|
-
readonly isOk: true;
|
|
5
|
-
readonly value: T;
|
|
6
|
-
} | {
|
|
7
|
-
readonly isError: true;
|
|
8
|
-
readonly isOk: false;
|
|
9
|
-
readonly error: E;
|
|
10
|
-
};
|
|
11
|
-
type AsyncResult<T, E extends Error> = Promise<Result<T, E>>;
|
|
12
|
-
declare function ok<T>(value: T): Result<T, never>;
|
|
13
|
-
declare function err<E extends Error>(error: E): Result<never, E>;
|
|
14
|
-
//#endregion
|
|
15
|
-
export { AsyncResult, Result, err, ok };
|
|
16
|
-
//# sourceMappingURL=index.d.ts.map
|
package/build/index.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
//#region src/index.ts
|
|
2
|
-
/**
|
|
3
|
-
* Creates a successful Result with the given value.
|
|
4
|
-
* @param value The success value
|
|
5
|
-
* @returns A Result object representing success
|
|
6
|
-
*/
|
|
7
|
-
function ok(value) {
|
|
8
|
-
return {
|
|
9
|
-
isError: false,
|
|
10
|
-
isOk: true,
|
|
11
|
-
value
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Creates an error Result with the given error.
|
|
16
|
-
* @param error The error value
|
|
17
|
-
* @returns A Result object representing error
|
|
18
|
-
*/
|
|
19
|
-
function err(error) {
|
|
20
|
-
return {
|
|
21
|
-
isError: true,
|
|
22
|
-
isOk: false,
|
|
23
|
-
error
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
//#endregion
|
|
28
|
-
export { err, ok };
|
|
29
|
-
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Result type to represent the outcome of an operation.\n * It can either be a success with a value or an error.\n * This is a generic type that can be used with any type of value and error (should extend Error).\n *\n * It is also an alias object containing the ok and error functions to\n * make it easier to create Result objects.\n */\nexport type Result<T, E extends Error> =\n | { readonly isError: false; readonly isOk: true; readonly value: T }\n | { readonly isError: true; readonly isOk: false; readonly error: E };\n\n/**\n * Async version of Result type that wraps a Result in a Promise.\n */\nexport type AsyncResult<T, E extends Error> = Promise<Result<T, 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 */\nexport function ok<T>(value: T): Result<T, never> {\n return { isError: false, isOk: true, 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 */\nexport function err<E extends Error>(error: E): Result<never, E> {\n return { isError: true, isOk: false, error };\n}\n"],"mappings":";;;;;;AAsBA,SAAgB,GAAM,OAA4B;AAChD,QAAO;EAAE,SAAS;EAAO,MAAM;EAAM;EAAO;;;;;;;AAQ9C,SAAgB,IAAqB,OAA4B;AAC/D,QAAO;EAAE,SAAS;EAAM,MAAM;EAAO;EAAO"}
|