errore 0.1.0 → 0.3.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 +307 -0
- package/dist/index.d.mts +16 -16
- package/dist/index.d.ts +16 -16
- package/package.json +5 -3
package/README.md
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# errore
|
|
2
|
+
|
|
3
|
+
Type-safe errors as values for TypeScript. Like Go, but with full type inference.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
Instead of wrapping values in a `Result<T, E>` type, functions simply return `E | T`. TypeScript's type narrowing handles the rest:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// Go-style: errors as values
|
|
11
|
+
const user = await fetchUser(id)
|
|
12
|
+
if (isError(user)) return user // TypeScript narrows type
|
|
13
|
+
console.log(user.name) // user is now User, not Error | User
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install errore
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { tryAsync, isError, TaggedError, matchError } from 'errore'
|
|
26
|
+
|
|
27
|
+
// Define typed errors
|
|
28
|
+
class NotFoundError extends TaggedError('NotFoundError')<{
|
|
29
|
+
id: string
|
|
30
|
+
message: string
|
|
31
|
+
}>() {
|
|
32
|
+
constructor(args: { id: string }) {
|
|
33
|
+
super({ ...args, message: `User ${args.id} not found` })
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class DbError extends TaggedError('DbError')<{
|
|
38
|
+
message: string
|
|
39
|
+
cause: unknown
|
|
40
|
+
}>() {}
|
|
41
|
+
|
|
42
|
+
// Function returns Error | Value (no wrapper!)
|
|
43
|
+
async function getUser(id: string): Promise<NotFoundError | DbError | User> {
|
|
44
|
+
const result = await tryAsync({
|
|
45
|
+
try: () => db.query(id),
|
|
46
|
+
catch: e => new DbError({ message: 'Query failed', cause: e })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
if (isError(result)) return result
|
|
50
|
+
if (!result) return new NotFoundError({ id })
|
|
51
|
+
|
|
52
|
+
return result
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Caller handles errors explicitly
|
|
56
|
+
const user = await getUser('123')
|
|
57
|
+
|
|
58
|
+
if (isError(user)) {
|
|
59
|
+
matchError(user, {
|
|
60
|
+
NotFoundError: e => console.log(`User ${e.id} not found`),
|
|
61
|
+
DbError: e => console.log(`Database error: ${e.message}`)
|
|
62
|
+
})
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// TypeScript knows: user is User
|
|
67
|
+
console.log(user.name)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### Type Guards
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { isError, isOk } from 'errore'
|
|
76
|
+
|
|
77
|
+
const result: NetworkError | User = await fetchUser(id)
|
|
78
|
+
|
|
79
|
+
if (isError(result)) {
|
|
80
|
+
// result is NetworkError
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
83
|
+
// result is User
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Try Functions
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { tryFn, tryAsync } from 'errore'
|
|
90
|
+
|
|
91
|
+
// Sync - wraps exceptions in UnhandledError
|
|
92
|
+
const parsed = tryFn(() => JSON.parse(input))
|
|
93
|
+
|
|
94
|
+
// Sync - with custom error type
|
|
95
|
+
const parsed = tryFn({
|
|
96
|
+
try: () => JSON.parse(input),
|
|
97
|
+
catch: e => new ParseError({ cause: e })
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Async
|
|
101
|
+
const response = await tryAsync(() => fetch(url))
|
|
102
|
+
|
|
103
|
+
// Async - with custom error
|
|
104
|
+
const response = await tryAsync({
|
|
105
|
+
try: () => fetch(url),
|
|
106
|
+
catch: e => new NetworkError({ cause: e })
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Transformations
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { map, mapError, andThen, tap } from 'errore'
|
|
114
|
+
|
|
115
|
+
// Transform value (if not error)
|
|
116
|
+
const name = map(user, u => u.name)
|
|
117
|
+
|
|
118
|
+
// Transform error
|
|
119
|
+
const appError = mapError(dbError, e => new AppError({ cause: e }))
|
|
120
|
+
|
|
121
|
+
// Chain operations
|
|
122
|
+
const posts = andThen(user, u => fetchPosts(u.id))
|
|
123
|
+
|
|
124
|
+
// Side effects
|
|
125
|
+
const logged = tap(user, u => console.log('Got user:', u.name))
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Extraction
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { unwrap, unwrapOr, match, partition } from 'errore'
|
|
132
|
+
|
|
133
|
+
// Extract or throw
|
|
134
|
+
const user = unwrap(result)
|
|
135
|
+
const user = unwrap(result, 'Custom error message')
|
|
136
|
+
|
|
137
|
+
// Extract or fallback
|
|
138
|
+
const name = unwrapOr(result, 'Anonymous')
|
|
139
|
+
|
|
140
|
+
// Pattern match
|
|
141
|
+
const message = match(result, {
|
|
142
|
+
ok: user => `Hello, ${user.name}`,
|
|
143
|
+
err: error => `Failed: ${error.message}`
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Split array into [successes, errors]
|
|
147
|
+
const [users, errors] = partition(results)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Tagged Errors
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { TaggedError, matchError, matchErrorPartial } from 'errore'
|
|
154
|
+
|
|
155
|
+
// Define errors with _tag discriminant
|
|
156
|
+
class ValidationError extends TaggedError('ValidationError')<{
|
|
157
|
+
field: string
|
|
158
|
+
message: string
|
|
159
|
+
}>() {}
|
|
160
|
+
|
|
161
|
+
class NetworkError extends TaggedError('NetworkError')<{
|
|
162
|
+
url: string
|
|
163
|
+
message: string
|
|
164
|
+
}>() {}
|
|
165
|
+
|
|
166
|
+
type AppError = ValidationError | NetworkError
|
|
167
|
+
|
|
168
|
+
// Exhaustive matching (TypeScript ensures all cases handled)
|
|
169
|
+
matchError(error, {
|
|
170
|
+
ValidationError: e => `Invalid ${e.field}`,
|
|
171
|
+
NetworkError: e => `Failed to fetch ${e.url}`
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Partial matching with fallback
|
|
175
|
+
matchErrorPartial(error, {
|
|
176
|
+
ValidationError: e => `Invalid ${e.field}`
|
|
177
|
+
}, e => `Unknown error: ${e.message}`)
|
|
178
|
+
|
|
179
|
+
// Type guards
|
|
180
|
+
ValidationError.is(value) // specific class
|
|
181
|
+
TaggedError.is(value) // any tagged error
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## How Type Safety Works
|
|
185
|
+
|
|
186
|
+
TypeScript narrows types after `instanceof Error` checks:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
function example(result: NetworkError | User): string {
|
|
190
|
+
if (result instanceof Error) {
|
|
191
|
+
// TypeScript knows: result is NetworkError
|
|
192
|
+
return result.message
|
|
193
|
+
}
|
|
194
|
+
// TypeScript knows: result is User (Error excluded)
|
|
195
|
+
return result.name
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
This works because:
|
|
200
|
+
1. `Error` is a built-in class TypeScript understands
|
|
201
|
+
2. Custom error classes extend `Error`
|
|
202
|
+
3. After an `instanceof Error` check, TS excludes all Error subtypes
|
|
203
|
+
|
|
204
|
+
## Result + Option Combined: `Error | T | null`
|
|
205
|
+
|
|
206
|
+
One of errore's best features: you can naturally combine error handling with optional values. No wrapper nesting needed!
|
|
207
|
+
|
|
208
|
+
In Rust, you'd need `Result<Option<T>, E>` or `Option<Result<T, E>>` and worry about the order. Here it's just a union:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
// Result + Option in one natural type
|
|
212
|
+
function findUser(id: string): NotFoundError | User | null {
|
|
213
|
+
if (id === 'bad') return new NotFoundError({ id })
|
|
214
|
+
if (id === 'missing') return null
|
|
215
|
+
return { id, name: 'Alice' }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const user = findUser('123')
|
|
219
|
+
|
|
220
|
+
// Handle error first
|
|
221
|
+
if (isError(user)) {
|
|
222
|
+
return user.message // TypeScript: user is NotFoundError
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Handle null/missing case - use ?. and ?? naturally!
|
|
226
|
+
const name = user?.name ?? 'Anonymous'
|
|
227
|
+
|
|
228
|
+
// Or check explicitly
|
|
229
|
+
if (user === null) {
|
|
230
|
+
return 'User not found'
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// TypeScript knows: user is User
|
|
234
|
+
console.log(user.name)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Works with `undefined` too
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
function lookup(key: string): NetworkError | string | undefined {
|
|
241
|
+
if (key === 'fail') return new NetworkError({ url: '/api', message: 'Failed' })
|
|
242
|
+
if (key === 'missing') return undefined
|
|
243
|
+
return 'found-value'
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const value = lookup('key')
|
|
247
|
+
|
|
248
|
+
if (isError(value)) return value
|
|
249
|
+
|
|
250
|
+
// ?? works naturally with undefined
|
|
251
|
+
const result = value ?? 'default'
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Triple union: `Error | T | null | undefined`
|
|
255
|
+
|
|
256
|
+
Even this works with full type inference:
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
function query(sql: string): ValidationError | { rows: string[] } | null | undefined {
|
|
260
|
+
if (sql === 'invalid') return new ValidationError({ field: 'sql', message: 'Bad' })
|
|
261
|
+
if (sql === 'empty') return null // explicitly no data
|
|
262
|
+
if (sql === 'no-table') return undefined // table doesn't exist
|
|
263
|
+
return { rows: ['a', 'b'] }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result = query('SELECT *')
|
|
267
|
+
|
|
268
|
+
if (isError(result)) {
|
|
269
|
+
return result.field // TypeScript: ValidationError
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (result == null) {
|
|
273
|
+
return 'no data' // handles both null and undefined
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// TypeScript: { rows: string[] }
|
|
277
|
+
console.log(result.rows)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Why this is better than Rust/Zig
|
|
281
|
+
|
|
282
|
+
| Language | Result + Option | Order matters? |
|
|
283
|
+
|----------|-----------------|----------------|
|
|
284
|
+
| Rust | `Result<Option<T>, E>` or `Option<Result<T, E>>` | Yes, must unwrap in order |
|
|
285
|
+
| Zig | `!?T` (error union + optional) | Yes, specific syntax |
|
|
286
|
+
| **errore** | `Error \| T \| null` | **No!** Check in any order |
|
|
287
|
+
|
|
288
|
+
With errore:
|
|
289
|
+
- Use `?.` and `??` naturally
|
|
290
|
+
- Check `isError()` or `=== null` in any order
|
|
291
|
+
- No unwrapping ceremony
|
|
292
|
+
- TypeScript infers everything
|
|
293
|
+
|
|
294
|
+
## Comparison with Result Types
|
|
295
|
+
|
|
296
|
+
| Result Pattern | errore |
|
|
297
|
+
|---------------|--------|
|
|
298
|
+
| `Result.ok(value)` | just `return value` |
|
|
299
|
+
| `Result.err(error)` | just `return error` |
|
|
300
|
+
| `result.value` | direct access after guard |
|
|
301
|
+
| `result.map(fn)` | `map(result, fn)` |
|
|
302
|
+
| `Result<User, Error>` | `Error \| User` |
|
|
303
|
+
| `Result<Option<T>, E>` | `Error \| T \| null` |
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -112,7 +112,7 @@ declare class UnhandledError extends UnhandledError_base {
|
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
114
|
* Type guard: checks if value is an Error.
|
|
115
|
-
* After this check, TypeScript narrows the type.
|
|
115
|
+
* After this check, TypeScript narrows the type to the error types in the union.
|
|
116
116
|
*
|
|
117
117
|
* @example
|
|
118
118
|
* const result = await fetchUser(id)
|
|
@@ -123,7 +123,7 @@ declare class UnhandledError extends UnhandledError_base {
|
|
|
123
123
|
* // result is narrowed to User
|
|
124
124
|
* console.log(result.name)
|
|
125
125
|
*/
|
|
126
|
-
declare function isError<
|
|
126
|
+
declare function isError<V>(value: V): value is Extract<V, Error>;
|
|
127
127
|
/**
|
|
128
128
|
* Type guard: checks if value is NOT an Error.
|
|
129
129
|
* Inverse of isError for convenience.
|
|
@@ -134,7 +134,7 @@ declare function isError<T, E extends Error>(value: E | T): value is E;
|
|
|
134
134
|
* console.log(result.name) // result is User
|
|
135
135
|
* }
|
|
136
136
|
*/
|
|
137
|
-
declare function isOk<
|
|
137
|
+
declare function isOk<V>(value: V): value is Exclude<V, Error>;
|
|
138
138
|
/**
|
|
139
139
|
* Execute a sync function and return either the value or an error.
|
|
140
140
|
*
|
|
@@ -187,7 +187,7 @@ declare function tryAsync<T, E extends Error>(opts: {
|
|
|
187
187
|
* // If user is User, result is string
|
|
188
188
|
* // If user is NotFoundError, result is NotFoundError
|
|
189
189
|
*/
|
|
190
|
-
declare function map<
|
|
190
|
+
declare function map<V, U>(value: V, fn: (v: Exclude<V, Error>) => U): Extract<V, Error> | U;
|
|
191
191
|
/**
|
|
192
192
|
* Transform the error if it is an error.
|
|
193
193
|
* If the value is not an error, returns it unchanged.
|
|
@@ -196,7 +196,7 @@ declare function map<T, U, E extends Error>(value: E | T, fn: (v: T) => U): E |
|
|
|
196
196
|
* const result = mapError(fetchResult, e => new AppError({ cause: e }))
|
|
197
197
|
* // Converts any error type to AppError
|
|
198
198
|
*/
|
|
199
|
-
declare function mapError<
|
|
199
|
+
declare function mapError<V, E2 extends Error>(value: V, fn: (e: Extract<V, Error>) => E2): E2 | Exclude<V, Error>;
|
|
200
200
|
/**
|
|
201
201
|
* Chain another errore-returning function.
|
|
202
202
|
* If the value is an error, returns it unchanged.
|
|
@@ -207,7 +207,7 @@ declare function mapError<T, E extends Error, E2 extends Error>(value: E | T, fn
|
|
|
207
207
|
* // If userId is ValidationError, result is ValidationError
|
|
208
208
|
* // If userId is string, result is whatever fetchUser returns
|
|
209
209
|
*/
|
|
210
|
-
declare function andThen<
|
|
210
|
+
declare function andThen<V, R>(value: V, fn: (v: Exclude<V, Error>) => R): Extract<V, Error> | R;
|
|
211
211
|
/**
|
|
212
212
|
* Async version of andThen.
|
|
213
213
|
*
|
|
@@ -217,7 +217,7 @@ declare function andThen<T, U, E extends Error, E2 extends Error>(value: E | T,
|
|
|
217
217
|
* return user
|
|
218
218
|
* })
|
|
219
219
|
*/
|
|
220
|
-
declare function andThenAsync<
|
|
220
|
+
declare function andThenAsync<V, R>(value: V, fn: (v: Exclude<V, Error>) => Promise<R>): Promise<Extract<V, Error> | R>;
|
|
221
221
|
/**
|
|
222
222
|
* Run a side effect if the value is not an error.
|
|
223
223
|
* Returns the original value unchanged.
|
|
@@ -225,7 +225,7 @@ declare function andThenAsync<T, U, E extends Error, E2 extends Error>(value: E
|
|
|
225
225
|
* @example
|
|
226
226
|
* const result = tap(user, u => console.log('Got user:', u.name))
|
|
227
227
|
*/
|
|
228
|
-
declare function tap<
|
|
228
|
+
declare function tap<V>(value: V, fn: (v: Exclude<V, Error>) => void): V;
|
|
229
229
|
/**
|
|
230
230
|
* Async version of tap.
|
|
231
231
|
*
|
|
@@ -234,7 +234,7 @@ declare function tap<T, E extends Error>(value: E | T, fn: (v: T) => void): E |
|
|
|
234
234
|
* await logToService(u)
|
|
235
235
|
* })
|
|
236
236
|
*/
|
|
237
|
-
declare function tapAsync<
|
|
237
|
+
declare function tapAsync<V>(value: V, fn: (v: Exclude<V, Error>) => Promise<void>): Promise<V>;
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
240
|
* Extract the value or throw if it's an error.
|
|
@@ -246,7 +246,7 @@ declare function tapAsync<T, E extends Error>(value: E | T, fn: (v: T) => Promis
|
|
|
246
246
|
* @example With custom message
|
|
247
247
|
* const user = unwrap(result, 'Failed to get user')
|
|
248
248
|
*/
|
|
249
|
-
declare function unwrap<
|
|
249
|
+
declare function unwrap<V>(value: V, message?: string): Exclude<V, Error>;
|
|
250
250
|
/**
|
|
251
251
|
* Extract the value or return a fallback if it's an error.
|
|
252
252
|
*
|
|
@@ -255,7 +255,7 @@ declare function unwrap<T, E extends Error>(value: E | T, message?: string): T;
|
|
|
255
255
|
* // If result is User, returns user
|
|
256
256
|
* // If result is Error, returns 'Anonymous'
|
|
257
257
|
*/
|
|
258
|
-
declare function unwrapOr<
|
|
258
|
+
declare function unwrapOr<V, U>(value: V, fallback: U): Exclude<V, Error> | U;
|
|
259
259
|
/**
|
|
260
260
|
* Pattern match on an errore value.
|
|
261
261
|
* Handles both success and error cases.
|
|
@@ -266,9 +266,9 @@ declare function unwrapOr<T, U, E extends Error>(value: E | T, fallback: U): T |
|
|
|
266
266
|
* err: error => `Failed: ${error.message}`
|
|
267
267
|
* })
|
|
268
268
|
*/
|
|
269
|
-
declare function match<
|
|
270
|
-
ok: (v:
|
|
271
|
-
err: (e:
|
|
269
|
+
declare function match<V, R>(value: V, handlers: {
|
|
270
|
+
ok: (v: Exclude<V, Error>) => R;
|
|
271
|
+
err: (e: Extract<V, Error>) => R;
|
|
272
272
|
}): R;
|
|
273
273
|
/**
|
|
274
274
|
* Partition an array of errore values into [successes, errors].
|
|
@@ -277,7 +277,7 @@ declare function match<T, E extends Error, R>(value: E | T, handlers: {
|
|
|
277
277
|
* const results = await Promise.all(ids.map(fetchUser))
|
|
278
278
|
* const [users, errors] = partition(results)
|
|
279
279
|
*/
|
|
280
|
-
declare function partition<
|
|
280
|
+
declare function partition<V>(values: V[]): [Exclude<V, Error>[], Extract<V, Error>[]];
|
|
281
281
|
/**
|
|
282
282
|
* Flatten a nested errore: (E1 | (E2 | T)) becomes (E1 | E2 | T).
|
|
283
283
|
* Useful when chaining operations that can fail.
|
|
@@ -286,6 +286,6 @@ declare function partition<T, E extends Error>(values: (E | T)[]): [T[], E[]];
|
|
|
286
286
|
* const nested: NetworkError | (ParseError | User) = await fetchAndParse()
|
|
287
287
|
* const flat: NetworkError | ParseError | User = flatten(nested)
|
|
288
288
|
*/
|
|
289
|
-
declare function flatten<
|
|
289
|
+
declare function flatten<V>(value: V): V;
|
|
290
290
|
|
|
291
291
|
export { type EnsureNotError, type Errore, type InferError, type InferValue, TaggedError, type TaggedErrorClass, type TaggedErrorInstance, UnhandledError, andThen, andThenAsync, flatten, isError, isOk, isTaggedError, map, mapError, match, matchError, matchErrorPartial, partition, tap, tapAsync, tryAsync, tryFn, unwrap, unwrapOr };
|
package/dist/index.d.ts
CHANGED
|
@@ -112,7 +112,7 @@ declare class UnhandledError extends UnhandledError_base {
|
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
114
|
* Type guard: checks if value is an Error.
|
|
115
|
-
* After this check, TypeScript narrows the type.
|
|
115
|
+
* After this check, TypeScript narrows the type to the error types in the union.
|
|
116
116
|
*
|
|
117
117
|
* @example
|
|
118
118
|
* const result = await fetchUser(id)
|
|
@@ -123,7 +123,7 @@ declare class UnhandledError extends UnhandledError_base {
|
|
|
123
123
|
* // result is narrowed to User
|
|
124
124
|
* console.log(result.name)
|
|
125
125
|
*/
|
|
126
|
-
declare function isError<
|
|
126
|
+
declare function isError<V>(value: V): value is Extract<V, Error>;
|
|
127
127
|
/**
|
|
128
128
|
* Type guard: checks if value is NOT an Error.
|
|
129
129
|
* Inverse of isError for convenience.
|
|
@@ -134,7 +134,7 @@ declare function isError<T, E extends Error>(value: E | T): value is E;
|
|
|
134
134
|
* console.log(result.name) // result is User
|
|
135
135
|
* }
|
|
136
136
|
*/
|
|
137
|
-
declare function isOk<
|
|
137
|
+
declare function isOk<V>(value: V): value is Exclude<V, Error>;
|
|
138
138
|
/**
|
|
139
139
|
* Execute a sync function and return either the value or an error.
|
|
140
140
|
*
|
|
@@ -187,7 +187,7 @@ declare function tryAsync<T, E extends Error>(opts: {
|
|
|
187
187
|
* // If user is User, result is string
|
|
188
188
|
* // If user is NotFoundError, result is NotFoundError
|
|
189
189
|
*/
|
|
190
|
-
declare function map<
|
|
190
|
+
declare function map<V, U>(value: V, fn: (v: Exclude<V, Error>) => U): Extract<V, Error> | U;
|
|
191
191
|
/**
|
|
192
192
|
* Transform the error if it is an error.
|
|
193
193
|
* If the value is not an error, returns it unchanged.
|
|
@@ -196,7 +196,7 @@ declare function map<T, U, E extends Error>(value: E | T, fn: (v: T) => U): E |
|
|
|
196
196
|
* const result = mapError(fetchResult, e => new AppError({ cause: e }))
|
|
197
197
|
* // Converts any error type to AppError
|
|
198
198
|
*/
|
|
199
|
-
declare function mapError<
|
|
199
|
+
declare function mapError<V, E2 extends Error>(value: V, fn: (e: Extract<V, Error>) => E2): E2 | Exclude<V, Error>;
|
|
200
200
|
/**
|
|
201
201
|
* Chain another errore-returning function.
|
|
202
202
|
* If the value is an error, returns it unchanged.
|
|
@@ -207,7 +207,7 @@ declare function mapError<T, E extends Error, E2 extends Error>(value: E | T, fn
|
|
|
207
207
|
* // If userId is ValidationError, result is ValidationError
|
|
208
208
|
* // If userId is string, result is whatever fetchUser returns
|
|
209
209
|
*/
|
|
210
|
-
declare function andThen<
|
|
210
|
+
declare function andThen<V, R>(value: V, fn: (v: Exclude<V, Error>) => R): Extract<V, Error> | R;
|
|
211
211
|
/**
|
|
212
212
|
* Async version of andThen.
|
|
213
213
|
*
|
|
@@ -217,7 +217,7 @@ declare function andThen<T, U, E extends Error, E2 extends Error>(value: E | T,
|
|
|
217
217
|
* return user
|
|
218
218
|
* })
|
|
219
219
|
*/
|
|
220
|
-
declare function andThenAsync<
|
|
220
|
+
declare function andThenAsync<V, R>(value: V, fn: (v: Exclude<V, Error>) => Promise<R>): Promise<Extract<V, Error> | R>;
|
|
221
221
|
/**
|
|
222
222
|
* Run a side effect if the value is not an error.
|
|
223
223
|
* Returns the original value unchanged.
|
|
@@ -225,7 +225,7 @@ declare function andThenAsync<T, U, E extends Error, E2 extends Error>(value: E
|
|
|
225
225
|
* @example
|
|
226
226
|
* const result = tap(user, u => console.log('Got user:', u.name))
|
|
227
227
|
*/
|
|
228
|
-
declare function tap<
|
|
228
|
+
declare function tap<V>(value: V, fn: (v: Exclude<V, Error>) => void): V;
|
|
229
229
|
/**
|
|
230
230
|
* Async version of tap.
|
|
231
231
|
*
|
|
@@ -234,7 +234,7 @@ declare function tap<T, E extends Error>(value: E | T, fn: (v: T) => void): E |
|
|
|
234
234
|
* await logToService(u)
|
|
235
235
|
* })
|
|
236
236
|
*/
|
|
237
|
-
declare function tapAsync<
|
|
237
|
+
declare function tapAsync<V>(value: V, fn: (v: Exclude<V, Error>) => Promise<void>): Promise<V>;
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
240
|
* Extract the value or throw if it's an error.
|
|
@@ -246,7 +246,7 @@ declare function tapAsync<T, E extends Error>(value: E | T, fn: (v: T) => Promis
|
|
|
246
246
|
* @example With custom message
|
|
247
247
|
* const user = unwrap(result, 'Failed to get user')
|
|
248
248
|
*/
|
|
249
|
-
declare function unwrap<
|
|
249
|
+
declare function unwrap<V>(value: V, message?: string): Exclude<V, Error>;
|
|
250
250
|
/**
|
|
251
251
|
* Extract the value or return a fallback if it's an error.
|
|
252
252
|
*
|
|
@@ -255,7 +255,7 @@ declare function unwrap<T, E extends Error>(value: E | T, message?: string): T;
|
|
|
255
255
|
* // If result is User, returns user
|
|
256
256
|
* // If result is Error, returns 'Anonymous'
|
|
257
257
|
*/
|
|
258
|
-
declare function unwrapOr<
|
|
258
|
+
declare function unwrapOr<V, U>(value: V, fallback: U): Exclude<V, Error> | U;
|
|
259
259
|
/**
|
|
260
260
|
* Pattern match on an errore value.
|
|
261
261
|
* Handles both success and error cases.
|
|
@@ -266,9 +266,9 @@ declare function unwrapOr<T, U, E extends Error>(value: E | T, fallback: U): T |
|
|
|
266
266
|
* err: error => `Failed: ${error.message}`
|
|
267
267
|
* })
|
|
268
268
|
*/
|
|
269
|
-
declare function match<
|
|
270
|
-
ok: (v:
|
|
271
|
-
err: (e:
|
|
269
|
+
declare function match<V, R>(value: V, handlers: {
|
|
270
|
+
ok: (v: Exclude<V, Error>) => R;
|
|
271
|
+
err: (e: Extract<V, Error>) => R;
|
|
272
272
|
}): R;
|
|
273
273
|
/**
|
|
274
274
|
* Partition an array of errore values into [successes, errors].
|
|
@@ -277,7 +277,7 @@ declare function match<T, E extends Error, R>(value: E | T, handlers: {
|
|
|
277
277
|
* const results = await Promise.all(ids.map(fetchUser))
|
|
278
278
|
* const [users, errors] = partition(results)
|
|
279
279
|
*/
|
|
280
|
-
declare function partition<
|
|
280
|
+
declare function partition<V>(values: V[]): [Exclude<V, Error>[], Extract<V, Error>[]];
|
|
281
281
|
/**
|
|
282
282
|
* Flatten a nested errore: (E1 | (E2 | T)) becomes (E1 | E2 | T).
|
|
283
283
|
* Useful when chaining operations that can fail.
|
|
@@ -286,6 +286,6 @@ declare function partition<T, E extends Error>(values: (E | T)[]): [T[], E[]];
|
|
|
286
286
|
* const nested: NetworkError | (ParseError | User) = await fetchAndParse()
|
|
287
287
|
* const flat: NetworkError | ParseError | User = flatten(nested)
|
|
288
288
|
*/
|
|
289
|
-
declare function flatten<
|
|
289
|
+
declare function flatten<V>(value: V): V;
|
|
290
290
|
|
|
291
291
|
export { type EnsureNotError, type Errore, type InferError, type InferValue, TaggedError, type TaggedErrorClass, type TaggedErrorInstance, UnhandledError, andThen, andThenAsync, flatten, isError, isOk, isTaggedError, map, mapError, match, matchError, matchErrorPartial, partition, tap, tapAsync, tryAsync, tryFn, unwrap, unwrapOr };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "errore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Type-safe errors as values for TypeScript. Like Go, but with full type inference.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "vitest"
|
|
21
22
|
},
|
|
22
23
|
"keywords": [
|
|
23
24
|
"error",
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
"license": "MIT",
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"tsup": "^8.0.0",
|
|
34
|
-
"typescript": "^5.0.0"
|
|
35
|
+
"typescript": "^5.0.0",
|
|
36
|
+
"vitest": "^3.2.4"
|
|
35
37
|
}
|
|
36
38
|
}
|