rsult 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/src/result.ts ADDED
@@ -0,0 +1,646 @@
1
+ import { Option, None, Some } from './option';
2
+
3
+ export type Result<T, E> = ResultOk<T, E> | ResultErr<T, E>;
4
+
5
+ export interface IResultCore<T, E> {
6
+ /**
7
+ * Checks if the result is an instance of `ResultOk`.
8
+ * @returns true if the result is `ResultOk`, otherwise false.
9
+ *
10
+ * Usage Example:
11
+ * const result = Ok(5);
12
+ * if (result.is_ok()) {
13
+ * console.log("Result is Ok");
14
+ * }
15
+ */
16
+ is_ok(): this is ResultOk<T, E>;
17
+
18
+ /**
19
+ * Checks if the result is an instance of `ResultErr`.
20
+ * @returns true if the result is `ResultErr`, otherwise false.
21
+ *
22
+ * Usage Example:
23
+ * const result = Err(new Error("Error"));
24
+ * if (result.is_err()) {
25
+ * console.log("Result is Err");
26
+ * }
27
+ */
28
+ is_err(): this is ResultErr<T, E>;
29
+
30
+ /**
31
+ * Retrieves the value from `ResultOk`, wrapped in an `Option`.
32
+ * @returns `Some` containing the value if the result is `ResultOk`, otherwise `None`.
33
+ *
34
+ * Usage Example:
35
+ * const result = Ok(5);
36
+ * const value = result.ok();
37
+ * if (value.is_some()) {
38
+ * console.log("Value:", value.unwrap());
39
+ * }
40
+ */
41
+ ok(): Option<T>;
42
+
43
+ /**
44
+ * Retrieves the error from `ResultErr`, wrapped in an `Option`.
45
+ * @returns `Some` containing the error if the result is `ResultErr`, otherwise `None`.
46
+ *
47
+ * Usage Example:
48
+ * const result = Err(new Error("Error"));
49
+ * const error = result.err();
50
+ * if (error.is_some()) {
51
+ * console.log("Error:", error.unwrap());
52
+ * }
53
+ */
54
+ err(): Option<E>;
55
+
56
+ /**
57
+ * Returns the contained `ResultOk` value, but throws an error with a provided message if
58
+ * the result is a `ResultErr`.
59
+ * @param msg The message to throw with if the result is an error.
60
+ * @returns The contained `ResultOk` value.
61
+ *
62
+ * Usage Example:
63
+ * const result = Ok(5);
64
+ * console.log(result.expect("This should not fail"));
65
+ */
66
+ expect(msg: string): T;
67
+
68
+ /**
69
+ * Unwraps a `ResultOk`, yielding the contained value.
70
+ * @returns The `ResultOk` value.
71
+ * @throws Throws if the result is `ResultErr`.
72
+ *
73
+ * Usage Example:
74
+ * const result = Ok(5);
75
+ * console.log(result.unwrap());
76
+ */
77
+ unwrap(): T;
78
+
79
+ /**
80
+ * Returns the contained `ResultErr` error, but throws an error with a provided message if
81
+ * the result is a `ResultOk`.
82
+ * @param msg The message to throw with if the result is Ok.
83
+ * @returns The contained `ResultErr` error.
84
+ *
85
+ * Usage Example:
86
+ * const result = Err(new Error("Failure"));
87
+ * console.log(result.expect_err("Expected an error"));
88
+ */
89
+ expect_err(msg: string): E;
90
+
91
+ /**
92
+ * Unwraps a `ResultErr`, yielding the contained error.
93
+ * @returns The `ResultErr` error.
94
+ * @throws Throws if the result is `ResultOk`.
95
+ *
96
+ * Usage Example:
97
+ * const result = Err(new Error("Failure"));
98
+ * console.log(result.unwrap_err());
99
+ */
100
+ unwrap_err(): E;
101
+
102
+ /**
103
+ * Converts from `IResultCore<T, E>` to `T`.
104
+ * @returns The contained `ResultOk` value.
105
+ * @throws Throws if the result is `ResultErr`.
106
+ *
107
+ * Usage Example:
108
+ * const result = Ok(5);
109
+ * console.log(result.into_ok());
110
+ */
111
+ into_ok(): T;
112
+
113
+ /**
114
+ * Converts from `IResultCore<T, E>` to `E`.
115
+ * @returns The contained `ResultErr` error.
116
+ * @throws Throws if the result is `ResultOk`.
117
+ *
118
+ * Usage Example:
119
+ * const result = Err(new Error("Failure"));
120
+ * console.log(result.into_err());
121
+ */
122
+ into_err(): E;
123
+ }
124
+
125
+ export interface IResultExt<T, E> extends IResultCore<T, E> {
126
+ /**
127
+ * Checks if the result is Ok and the contained value passes a specified condition.
128
+ * @param f A predicate to apply to the contained value if the result is Ok.
129
+ * @returns true if the result is Ok and the predicate returns true, otherwise false.
130
+ *
131
+ * Usage Examples:
132
+ * const result = Ok(5);
133
+ * if (result.is_ok_and(x => x > 3)) {
134
+ * console.log("Result is Ok and greater than 3");
135
+ * }
136
+ *
137
+ * const result = Ok(2);
138
+ * if (!result.is_ok_and(x => x > 3)) {
139
+ * console.log("Result is not Ok or not greater than 3");
140
+ * }
141
+ */
142
+ is_ok_and(f: (value: T) => boolean): boolean;
143
+
144
+ /**
145
+ * Checks if the result is Err and the contained error passes a specified condition.
146
+ * @param f A predicate to apply to the contained error if the result is Err.
147
+ * @returns true if the result is Err and the predicate returns true, otherwise false.
148
+ *
149
+ * Usage Examples:
150
+ * const result = Err(new Error("Network failure"));
151
+ * if (result.is_err_and(e => e.message.includes("Network"))) {
152
+ * console.log("Network error occurred");
153
+ * }
154
+ */
155
+ is_err_and(f: (value: E) => boolean): boolean;
156
+
157
+ /**
158
+ * Transforms the result via a mapping function if it is Ok.
159
+ * @param fn A function to transform the Ok value.
160
+ * @returns A new Result where the Ok value has been transformed.
161
+ *
162
+ * Usage Example:
163
+ * const result = Ok(5);
164
+ * const mapped = result.map(x => x * 2);
165
+ *
166
+ * const result = Err("Error");
167
+ * const mapped = result.map(x => x * 2); // remains Err
168
+ */
169
+ map<U>(fn: (arg: T) => U): Result<U, E>;
170
+
171
+ /**
172
+ * Transforms the result via a mapping function if it is Ok, otherwise returns a default value.
173
+ * @param defaultVal A default value to return if the result is Err.
174
+ * @param f A function to transform the Ok value.
175
+ * @returns The transformed Ok value or the default value.
176
+ *
177
+ * Usage Example:
178
+ * const result = Ok(5);
179
+ * const value = result.map_or(0, x => x * 2);
180
+ *
181
+ * const result = Err("Error");
182
+ * const value = result.map_or(0, x => x * 2); // 0
183
+ */
184
+ map_or<U>(defaultVal: U, f: (arg: T) => U): U;
185
+
186
+ /**
187
+ * Transforms the result via a mapping function if it is Ok, otherwise computes a default value using a function.
188
+ * @param defaultFunc A function to compute a default value if the result is Err.
189
+ * @param f A function to transform the Ok value.
190
+ * @returns The transformed Ok value or the computed default value.
191
+ *
192
+ * Usage Example:
193
+ * const result = Ok(5);
194
+ * const value = result.map_or_else(() => 0, x => x * 2);
195
+ *
196
+ * const result = Err("Error");
197
+ * const value = result.map_or_else(() => 0, x => x * 2); // 0
198
+ */
199
+ map_or_else<U>(defaultFunc: (err: E) => U, f: (arg: T) => U): U;
200
+
201
+ /**
202
+ * Maps a `Result<T, E>` to `Result<T, U>` by applying a function to a contained `Err` value, leaving an `Ok` value untouched.
203
+ * @param fn A function to transform the Err value.
204
+ * @returns A new Result where the Err has been transformed.
205
+ *
206
+ * Usage Example:
207
+ * const result = Err("Error");
208
+ * const mappedErr = result.map_err(e => new Error(e));
209
+ */
210
+ map_err<U>(fn: (arg: E) => U): Result<T, U>;
211
+
212
+ /**
213
+ * Applies a function to the contained value (if Ok), then returns the unmodified Result.
214
+ * @param f A function to apply to the Ok value.
215
+ * @returns The original Result.
216
+ *
217
+ * Usage Example:
218
+ * const result = Ok(5);
219
+ * result.inspect(x => console.log(`Value: ${x}`));
220
+ */
221
+ inspect(f: (val: T) => void): Result<T, E>;
222
+
223
+ /**
224
+ * Applies a function to the contained error (if Err), then returns the unmodified Result.
225
+ * @param f A function to apply to the Err value.
226
+ * @returns The original Result.
227
+ *
228
+ * Usage Example:
229
+ * const result = Err("Error");
230
+ * result.inspect_err(e => console.log(`Error: ${e}`));
231
+ */
232
+ inspect_err(f: (val: E) => void): Result<T, E>;
233
+
234
+ /**
235
+ * Returns `res` if the result is Ok, otherwise returns the Err value of `self`.
236
+ * @param res The result to return if `self` is Ok.
237
+ * @returns Either `res` or the original Err.
238
+ *
239
+ * Usage Example:
240
+ * const result = Ok(5);
241
+ * const other = Ok("Hello");
242
+ * const finalResult = result.and(other); // Ok("Hello")
243
+ */
244
+ and<U>(res: Result<U, E>): Result<U, E>;
245
+
246
+ /**
247
+ * Calls `fn` if the result is Ok, otherwise returns the Err value of `self`.
248
+ * @param fn A function to apply to the Ok value.
249
+ * @returns The result of `fn` if the original result is Ok, otherwise the Err.
250
+ *
251
+ * Usage Example:
252
+ * const result = Ok(5);
253
+ * const finalResult = result.and_then(x => Ok(x * 2)); // Ok(10)
254
+ */
255
+ and_then<U>(fn: (arg: T) => Result<U, E>): Result<U, E>;
256
+
257
+ /**
258
+ * Returns `res` if the result is Err, otherwise returns the Ok value of `self`.
259
+ * @param res The result to return if `self` is Err.
260
+ * @returns Either `res` or the original Ok.
261
+ *
262
+ * Usage Example:
263
+ * const result = Err("Error");
264
+ * const other = Ok(5);
265
+ * const finalResult = result.or(other); // Ok(5)
266
+ */
267
+ or<U>(res: Result<U, E>): Result<U, E>;
268
+
269
+ /**
270
+ * Calls `fn` if the result is Err, otherwise returns the Ok value of `self`.
271
+ * @param fn A function to apply to the Err value.
272
+ * @returns The result of `fn` if the original result is Err, otherwise the Ok.
273
+ *
274
+ * Usage Example:
275
+ * const result = Err("Error");
276
+ * const finalResult = result.or_else(e => Ok(`Handled ${e}`)); // Ok("Handled Error")
277
+ */
278
+ or_else<U>(fn: (arg: E) => Result<T, U>): Result<T, U>;
279
+
280
+ /**
281
+ * Returns the contained Ok value or a provided default.
282
+ * @param defaultVal The default value to return if the result is Err.
283
+ * @returns The Ok value or the default.
284
+ *
285
+ * Usage Example:
286
+ * const result = Ok(5);
287
+ * console.log(result.unwrap_or(0)); // 5
288
+ *
289
+ * const result = Err("Error");
290
+ * console.log(result.unwrap_or(0)); // 0
291
+ */
292
+ unwrap_or(defaultVal: T): T;
293
+
294
+ /**
295
+ * Returns the contained Ok value or computes it from a function.
296
+ * @param fn A function to compute the default value if the result is Err.
297
+ * @returns The Ok value or the computed one.
298
+ *
299
+ * Usage Example:
300
+ * const result = Err("Error");
301
+ * console.log(result.unwrap_or_else(() => 5)); // 5
302
+ */
303
+ unwrap_or_else(fn: (arg: E) => T): T;
304
+ }
305
+
306
+ type UnwrapResult<T> = T extends Result<infer U, any> ? U : T;
307
+
308
+ export interface IResultIteration<T, E> extends IResultCore<T, E> {
309
+ /**
310
+ * Returns an iterator over the potentially contained value.
311
+ * @returns An iterator which yields the contained value if it is `ResultOk<T, E>`.
312
+ *
313
+ * Usage Example:
314
+ * const okResult = Ok(5);
315
+ * for (const value of okResult.iter()) {
316
+ * console.log(value); // prints 5
317
+ * }
318
+ *
319
+ * const errResult = Err(new Error("error"));
320
+ * for (const value of errResult.iter()) {
321
+ * // This block will not be executed.
322
+ * }
323
+ */
324
+ iter(): IterableIterator<T>;
325
+
326
+ /**
327
+ * Attempts to transpose a `Result` of a `Promise` into a `Promise` of a `Result`.
328
+ * @returns A Promise of a Result if the inner value is a Promise, null otherwise.
329
+ *
330
+ * Usage Example:
331
+ * async function example() {
332
+ * const resultPromise = Ok(Promise.resolve(5));
333
+ * const transposed = resultPromise.transpose(); // Result<Promise<5>, E> -> Promise<Result<5, E>> | null
334
+ * console.log(await transposed); // Prints Ok(5) if the promise resolves successfully.
335
+ * }
336
+ */
337
+ transpose(): Result<T, E>;
338
+
339
+ /**
340
+ * Flattens a nested `Result` if the contained value is itself a `Result`.
341
+ * @returns A single-layer `Result`, by stripping one layer of `Result` container.
342
+ *
343
+ * Usage Example:
344
+ * const nestedOk = Ok(Ok(5));
345
+ * const flattened = nestedOk.flatten(); // Results in Ok(5)
346
+ *
347
+ * const nestedErr = Ok(Err(new Error("error")));
348
+ * const flattenedError = nestedErr.flatten(); // Results in Err(new Error("error"))
349
+ */
350
+ flatten(): Result<UnwrapResult<T>, E>;
351
+ }
352
+
353
+ export interface IResult<T, E> extends
354
+ IResultCore<T, E>,
355
+ IResultExt<T, E>,
356
+ IResultIteration<T, E> {}
357
+
358
+ export const isResultOk = <T, E>(val: any): val is ResultOk<T, E> => {
359
+ return val instanceof ResultOk;
360
+ }
361
+
362
+ export const isResultErr = <T, E>(val: any): val is ResultErr<T, E> => {
363
+ return val instanceof ResultErr;
364
+ }
365
+
366
+ export class ResultOk<T, E> implements IResult<T, E> {
367
+ private readonly _tag = 'Ok' as const;
368
+ private readonly _T!: T;
369
+ private readonly _E!: E;
370
+
371
+ constructor(readonly value: T) {
372
+ }
373
+
374
+ is_ok(): this is ResultOk<T, E> {
375
+ return true;
376
+ }
377
+
378
+ is_err(): this is never {
379
+ return false;
380
+ }
381
+
382
+ is_ok_and(f: (value: T) => boolean): boolean {
383
+ return f(this.value);
384
+ }
385
+
386
+ is_err_and(_f: (value: E) => boolean): boolean {
387
+ return false;
388
+ }
389
+
390
+ ok(): Option<T> {
391
+ return Some(this.value);
392
+ }
393
+
394
+ err(): Option<E> {
395
+ return None();
396
+ }
397
+
398
+ map<U>(fn: (arg: T) => U): Result<U, E> {
399
+ return new ResultOk<U, E>(fn(this.value));
400
+ }
401
+
402
+ map_or<U>(_d: U, f: (arg: T) => U): U {
403
+ return f(this.value);
404
+ }
405
+
406
+ map_or_else<U>(_d: (e: E) => U, f: (arg: T) => U): U {
407
+ return f(this.value);
408
+ }
409
+
410
+ map_err<U>(_fn: (arg: E) => U): Result<T, U> {
411
+ return this as any;
412
+ }
413
+
414
+ inspect(f: (val: T) => void): Result<T, E> {
415
+ f(this.value);
416
+
417
+ return this;
418
+ }
419
+
420
+ inspect_err(_f: (val: E) => void): Result<T, E> {
421
+ return this;
422
+ }
423
+
424
+ iter(): IterableIterator<T> {
425
+ return [this.value][Symbol.iterator]();
426
+ }
427
+
428
+ expect(_msg: string): T {
429
+ return this.value;
430
+ }
431
+
432
+ unwrap(): T {
433
+ return this.value;
434
+ }
435
+
436
+ //unwrap_or_default(): T {
437
+ // ! not implemented
438
+ //}
439
+
440
+ expect_err(msg: string): E {
441
+ throw new Error(msg);
442
+ }
443
+
444
+ unwrap_err(): never {
445
+ throw new Error('Called Result.unwrap_err() on an Ok value: ' + this.value);
446
+ }
447
+
448
+ and<U>(res: Result<U, E>): Result<U, E> {
449
+ return res;
450
+ }
451
+
452
+ and_then<U>(fn: (arg: T) => Result<U, E>): Result<U, E> {
453
+ return fn(this.value);
454
+ }
455
+
456
+ or<U>(_res: Result<U, E>): Result<U, E> {
457
+ return this as any;
458
+ }
459
+
460
+ or_else<U>(fn: (arg: E) => Result<T, U>): Result<T, U> {
461
+ return this as any;
462
+ }
463
+
464
+ unwrap_or(_optb: T): T {
465
+ return this.value;
466
+ }
467
+
468
+ unwrap_or_else(_fn: (arg: E) => T): T {
469
+ return this.value;
470
+ }
471
+
472
+ transpose(): Result<T, E> {
473
+ return new ResultOk<T, E>(this.value);
474
+ }
475
+
476
+ flatten(): Result<UnwrapResult<T>, E> {
477
+ if (this.value instanceof ResultOk || this.value instanceof ResultErr) {
478
+ return this.value;
479
+ } else {
480
+ // This case should not happen if T is always a Result,
481
+ // but it's here to satisfy TypeScript's type checker.
482
+ return new ResultOk<UnwrapResult<T>, E>(this.value as UnwrapResult<T>);
483
+ }
484
+ }
485
+
486
+ into_ok(): T {
487
+ return this.value;
488
+ }
489
+
490
+ into_err(): never {
491
+ throw new Error('Called Result.into_err() on an Ok value: ' + this.value);
492
+ }
493
+ }
494
+
495
+ export class ResultErr<T, E> implements IResult<T, E> {
496
+ private readonly _tag: 'Err' = 'Err';
497
+ private readonly _T!: T;
498
+ private readonly _E!: E;
499
+
500
+ constructor(readonly value: E) {
501
+ }
502
+
503
+ is_ok(): this is never {
504
+ return false;
505
+ }
506
+
507
+ is_err(): this is ResultErr<T, E> {
508
+ return true;
509
+ }
510
+
511
+ is_ok_and(_f: (value: T) => boolean): boolean {
512
+ return false;
513
+ }
514
+
515
+ is_err_and(f: (value: E) => boolean): boolean {
516
+ return f(this.value);
517
+ }
518
+
519
+ ok(): Option<T> {
520
+ return None();
521
+ }
522
+
523
+ err(): Option<E> {
524
+ return Some(this.value);
525
+ }
526
+
527
+ map<U>(_fn: (arg: T) => U): Result<U, E> {
528
+ return this as any;
529
+ }
530
+
531
+ map_or<U>(d: U, _f: (arg: T) => U): U {
532
+ return d;
533
+ }
534
+
535
+ map_or_else<U>(d: (e: E) => U, _f: (arg: T) => U): U {
536
+ return d(this.value);
537
+ }
538
+
539
+ map_err<U>(fn: (arg: E) => U): Result<T, U> {
540
+ return new ResultErr<T, U>(fn(this.value));
541
+ }
542
+
543
+ inspect(_f: (val: T) => void): Result<T, E> {
544
+ return this;
545
+ }
546
+
547
+ inspect_err(f: (val: E) => void): Result<T, E> {
548
+ f(this.value);
549
+ return this;
550
+ }
551
+
552
+ iter(): IterableIterator<T> {
553
+ return [][Symbol.iterator]();
554
+ }
555
+
556
+ expect(msg: string): never {
557
+ throw new Error(msg);
558
+ }
559
+
560
+ unwrap(): never {
561
+ throw new Error('Called Result.unwrap() on an Err value: ' + this.value);
562
+ }
563
+
564
+ //unwrap_or_default(): never {
565
+ // // ! not implemented
566
+ //}
567
+
568
+ expect_err(_msg: string): E {
569
+ return this.value;
570
+ }
571
+
572
+ unwrap_err(): E {
573
+ return this.value;
574
+ }
575
+
576
+ and<U>(_res: Result<U, E>): Result<U, E> {
577
+ return this as any;
578
+ }
579
+
580
+ and_then<U>(_fn: (arg: T) => Result<U, E>): Result<U, E> {
581
+ return this as any;
582
+ }
583
+
584
+ or<U>(res: Result<U, E>): Result<U, E> {
585
+ return res;
586
+ }
587
+
588
+ or_else<U>(fn: (arg: E) => Result<T, U>): Result<T, U> {
589
+ return fn(this.value);
590
+ }
591
+
592
+ unwrap_or(optb: T): T {
593
+ return optb;
594
+ }
595
+
596
+ unwrap_or_else(fn: (arg: E) => T): T {
597
+ return fn(this.value);
598
+ }
599
+
600
+ transpose(): Result<T, E> {
601
+ return new ResultErr<T, E>(this.value);
602
+ }
603
+
604
+ flatten(): Result<UnwrapResult<T>, E> {
605
+ return this.transpose() as Result<never, E>;
606
+ }
607
+
608
+ into_ok(): T {
609
+ throw new Error('Called Result.into_ok() on an Err value: ' + this.value);
610
+ }
611
+
612
+ into_err(): E {
613
+ return this.value;
614
+ }
615
+ }
616
+
617
+ export const Ok = <T, E>(val: T) => {
618
+ return new ResultOk<T, E>(val) as Result<T, never>;
619
+ };
620
+
621
+ export const Err = <T, E>(val: E) => {
622
+ return new ResultErr<T, E>(val) as Result<never, E>;
623
+ };
624
+
625
+ export const try_catch =
626
+ <T, E = Error>(
627
+ fn: () => T,
628
+ ): Result<T, E> => {
629
+ try {
630
+ return Ok(fn());
631
+ // @ts-ignore (error is nominally of type any / unknown, not Error)
632
+ } catch (error: Error) {
633
+ return Err(error);
634
+ }
635
+ };
636
+
637
+ export const result_from_promise =
638
+ async <T, E = Error>(
639
+ val: Promise<T>,
640
+ ): Promise<Result<T, E>> => {
641
+ try {
642
+ return new ResultOk<T, never>(await val);
643
+ } catch (error: unknown) {
644
+ return new ResultErr<never, E>(error as E);
645
+ }
646
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "es2020",
5
+ "lib": [
6
+ "es6",
7
+ "es7",
8
+ "esnext"
9
+ ],
10
+ "allowJs": false,
11
+ "moduleResolution": "node",
12
+ "forceConsistentCasingInFileNames": true,
13
+ "noImplicitReturns": true,
14
+ "noImplicitThis": true,
15
+ "noImplicitAny": true,
16
+ "strictNullChecks": true,
17
+ "declaration": true,
18
+ "sourceMap": true,
19
+ "allowSyntheticDefaultImports": true,
20
+ "suppressImplicitAnyIndexErrors": false,
21
+ "noUnusedLocals": false,
22
+ "strict": true,
23
+ "typeRoots": [
24
+ "./node_modules/@types/"
25
+ ]
26
+ },
27
+ "include": [
28
+ "src/**/*.ts",
29
+ "src/*.ts"
30
+ ],
31
+ "exclude": [
32
+ "node_modules/"
33
+ ]
34
+ }