@firtoz/maybe-error 1.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 +242 -0
- package/package.json +50 -0
- package/src/MaybeError.ts +45 -0
- package/src/index.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# @firtoz/maybe-error
|
|
2
|
+
|
|
3
|
+
Type-safe result handling with the MaybeError pattern for TypeScript.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Type-safe error handling** - Full TypeScript support with discriminated unions
|
|
8
|
+
- 🚀 **Zero dependencies** - Lightweight and fast
|
|
9
|
+
- 📦 **Tree-shakeable** - Import only what you need
|
|
10
|
+
- 🎯 **Simple API** - Easy to use and understand
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @firtoz/maybe-error
|
|
16
|
+
# or
|
|
17
|
+
yarn add @firtoz/maybe-error
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @firtoz/maybe-error
|
|
20
|
+
# or
|
|
21
|
+
bun add @firtoz/maybe-error
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { MaybeError, success, fail } from '@firtoz/maybe-error';
|
|
30
|
+
|
|
31
|
+
// Function that might fail
|
|
32
|
+
function divide(a: number, b: number): MaybeError<number> {
|
|
33
|
+
if (b === 0) {
|
|
34
|
+
return fail("Division by zero");
|
|
35
|
+
}
|
|
36
|
+
return success(a / b);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Usage
|
|
40
|
+
const result = divide(10, 2);
|
|
41
|
+
|
|
42
|
+
if (result.success) {
|
|
43
|
+
console.log("Result:", result.result); // TypeScript knows result exists
|
|
44
|
+
} else {
|
|
45
|
+
console.error("Error:", result.error); // TypeScript knows error exists
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### With Custom Error Types
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { MaybeError, success, fail } from '@firtoz/maybe-error';
|
|
53
|
+
|
|
54
|
+
type ValidationError = {
|
|
55
|
+
field: string;
|
|
56
|
+
message: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function validateEmail(email: string): MaybeError<string, ValidationError> {
|
|
60
|
+
if (!email.includes('@')) {
|
|
61
|
+
return fail({
|
|
62
|
+
field: 'email',
|
|
63
|
+
message: 'Email must contain @ symbol'
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return success(email);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const result = validateEmail("user@example.com");
|
|
70
|
+
|
|
71
|
+
if (result.success) {
|
|
72
|
+
console.log("Valid email:", result.result);
|
|
73
|
+
} else {
|
|
74
|
+
console.error(`Validation error in ${result.error.field}: ${result.error.message}`);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Async Functions
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { MaybeError, success, fail } from '@firtoz/maybe-error';
|
|
82
|
+
|
|
83
|
+
async function fetchUser(id: string): Promise<MaybeError<User, string>> {
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(`/api/users/${id}`);
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
return fail(`HTTP ${response.status}: ${response.statusText}`);
|
|
88
|
+
}
|
|
89
|
+
const user = await response.json();
|
|
90
|
+
return success(user);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return fail(error instanceof Error ? error.message : 'Unknown error');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Usage
|
|
97
|
+
const userResult = await fetchUser("123");
|
|
98
|
+
if (userResult.success) {
|
|
99
|
+
console.log("User:", userResult.result);
|
|
100
|
+
} else {
|
|
101
|
+
console.error("Failed to fetch user:", userResult.error);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## API Reference
|
|
106
|
+
|
|
107
|
+
### Types
|
|
108
|
+
|
|
109
|
+
#### `MaybeError<T, TError>`
|
|
110
|
+
|
|
111
|
+
A discriminated union type representing either success or failure.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
type MaybeError<T = undefined, TError = string> =
|
|
115
|
+
| DefiniteSuccess<T>
|
|
116
|
+
| DefiniteError<TError>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `DefiniteSuccess<T>`
|
|
120
|
+
|
|
121
|
+
Represents a successful result.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
type DefiniteSuccess<T = undefined> = {
|
|
125
|
+
success: true;
|
|
126
|
+
} & (T extends undefined ? { result?: T } : { result: T });
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### `DefiniteError<TError>`
|
|
130
|
+
|
|
131
|
+
Represents a failed result.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
type DefiniteError<TError = string> = {
|
|
135
|
+
success: false;
|
|
136
|
+
error: TError;
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `AssumeSuccess<T>`
|
|
141
|
+
|
|
142
|
+
Utility type to extract the success type from a MaybeError.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
type AssumeSuccess<T extends MaybeError<unknown>> = // extracted success type
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Functions
|
|
149
|
+
|
|
150
|
+
#### `success<T>(result?: T): DefiniteSuccess<T>`
|
|
151
|
+
|
|
152
|
+
Creates a success result.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const result1 = success(); // No result value
|
|
156
|
+
const result2 = success("Hello"); // With result value
|
|
157
|
+
const result3 = success({ id: 1, name: "John" }); // With object result
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### `fail<TError>(error: TError): DefiniteError<TError>`
|
|
161
|
+
|
|
162
|
+
Creates a failure result.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const error1 = fail("Something went wrong");
|
|
166
|
+
const error2 = fail({ code: 404, message: "Not found" });
|
|
167
|
+
const error3 = fail(new Error("Custom error"));
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
### Chaining Operations
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { MaybeError, success, fail } from '@firtoz/maybe-error';
|
|
176
|
+
|
|
177
|
+
function parseNumber(str: string): MaybeError<number> {
|
|
178
|
+
const num = Number(str);
|
|
179
|
+
if (isNaN(num)) {
|
|
180
|
+
return fail(`"${str}" is not a valid number`);
|
|
181
|
+
}
|
|
182
|
+
return success(num);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function sqrt(num: number): MaybeError<number> {
|
|
186
|
+
if (num < 0) {
|
|
187
|
+
return fail("Cannot calculate square root of negative number");
|
|
188
|
+
}
|
|
189
|
+
return success(Math.sqrt(num));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Chain operations
|
|
193
|
+
function parseAndSqrt(str: string): MaybeError<number> {
|
|
194
|
+
const parseResult = parseNumber(str);
|
|
195
|
+
if (!parseResult.success) {
|
|
196
|
+
return parseResult; // Forward the error
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return sqrt(parseResult.result);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Usage
|
|
203
|
+
const result = parseAndSqrt("16");
|
|
204
|
+
if (result.success) {
|
|
205
|
+
console.log("Square root:", result.result); // 4
|
|
206
|
+
} else {
|
|
207
|
+
console.error("Error:", result.error);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### With Promise.all
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { MaybeError, success, fail } from '@firtoz/maybe-error';
|
|
215
|
+
|
|
216
|
+
async function fetchMultipleUsers(ids: string[]): Promise<MaybeError<User[]>> {
|
|
217
|
+
try {
|
|
218
|
+
const promises = ids.map(id => fetchUser(id));
|
|
219
|
+
const results = await Promise.all(promises);
|
|
220
|
+
|
|
221
|
+
// Check if any failed
|
|
222
|
+
const errors = results.filter(r => !r.success);
|
|
223
|
+
if (errors.length > 0) {
|
|
224
|
+
return fail(`Failed to fetch ${errors.length} users`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// All succeeded, extract results
|
|
228
|
+
const users = results.map(r => r.success ? r.result : null).filter(Boolean);
|
|
229
|
+
return success(users as User[]);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return fail(error instanceof Error ? error.message : 'Unknown error');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Contributing
|
|
237
|
+
|
|
238
|
+
Contributions are welcome! This package is part of the [router-toolkit monorepo](https://github.com/firtoz/router-toolkit).
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT © [Firtina Ozbalikchi](https://github.com/firtoz)
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firtoz/maybe-error",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Type-safe result handling with MaybeError pattern",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"module": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"import": "./src/index.ts",
|
|
12
|
+
"require": "./src/index.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/**/*",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "echo 'No build step - using TypeScript source directly'",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "biome check src",
|
|
23
|
+
"format": "biome format src --write",
|
|
24
|
+
"test": "echo 'No tests yet'"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"typescript",
|
|
28
|
+
"error-handling",
|
|
29
|
+
"result-pattern",
|
|
30
|
+
"maybe-error",
|
|
31
|
+
"type-safe"
|
|
32
|
+
],
|
|
33
|
+
"author": "Firtina Ozbalikchi <firtoz@github.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"homepage": "https://github.com/firtoz/router-toolkit#readme",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/firtoz/router-toolkit.git",
|
|
39
|
+
"directory": "packages/maybe-error"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/firtoz/router-toolkit/issues"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type DefiniteError<TError = string> = {
|
|
2
|
+
success: false;
|
|
3
|
+
error: TError;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type DefiniteSuccess<T = undefined> = {
|
|
7
|
+
success: true;
|
|
8
|
+
} & (T extends undefined
|
|
9
|
+
? {
|
|
10
|
+
result?: T;
|
|
11
|
+
}
|
|
12
|
+
: {
|
|
13
|
+
result: T;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type MaybeError<T = undefined, TError = string> =
|
|
17
|
+
| DefiniteSuccess<T>
|
|
18
|
+
| DefiniteError<TError>;
|
|
19
|
+
|
|
20
|
+
export type AssumeSuccess<T extends MaybeError<unknown>> = Exclude<
|
|
21
|
+
T,
|
|
22
|
+
undefined
|
|
23
|
+
> extends MaybeError<infer U>
|
|
24
|
+
? U
|
|
25
|
+
: never;
|
|
26
|
+
|
|
27
|
+
export const success = <T = undefined>(
|
|
28
|
+
...params: T extends undefined ? [] : [T]
|
|
29
|
+
): DefiniteSuccess<T> => {
|
|
30
|
+
if (params.length === 0) {
|
|
31
|
+
return { success: true } as unknown as DefiniteSuccess<T>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
result: params[0],
|
|
37
|
+
} as unknown as DefiniteSuccess<T>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const fail = <TError = string>(error: TError): DefiniteError<TError> => {
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
error,
|
|
44
|
+
};
|
|
45
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./MaybeError";
|