errore 0.2.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 +91 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -201,6 +201,96 @@ This works because:
|
|
|
201
201
|
2. Custom error classes extend `Error`
|
|
202
202
|
3. After an `instanceof Error` check, TS excludes all Error subtypes
|
|
203
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
|
+
|
|
204
294
|
## Comparison with Result Types
|
|
205
295
|
|
|
206
296
|
| Result Pattern | errore |
|
|
@@ -210,6 +300,7 @@ This works because:
|
|
|
210
300
|
| `result.value` | direct access after guard |
|
|
211
301
|
| `result.map(fn)` | `map(result, fn)` |
|
|
212
302
|
| `Result<User, Error>` | `Error \| User` |
|
|
303
|
+
| `Result<Option<T>, E>` | `Error \| T \| null` |
|
|
213
304
|
|
|
214
305
|
## License
|
|
215
306
|
|