A social knowledge tool for researchers built on ATProto
1// Base Result types using discriminated union
2
3/**
4 * Represents a successful operation outcome.
5 * Contains the successful value of type T.
6 * E represents the potential error type for compatibility in the Result union.
7 */
8export class Ok<T, E> {
9 public readonly value: T;
10
11 constructor(value: T) {
12 this.value = value;
13 }
14
15 /** Type guard indicating success */
16 public isOk(): this is Ok<T, E> {
17 return true;
18 }
19
20 /** Type guard indicating failure */
21 public isErr(): this is Err<T, E> {
22 return false;
23 }
24
25 public unwrap(): T {
26 return this.value;
27 }
28}
29
30/**
31 * Represents a failed operation outcome.
32 * Contains the error value of type E.
33 * T represents the potential success type for compatibility in the Result union.
34 */
35export class Err<T, E> {
36 public readonly error: E;
37
38 constructor(error: E) {
39 this.error = error;
40 }
41
42 /** Type guard indicating success */
43 public isOk(): this is Ok<T, E> {
44 return false;
45 }
46
47 /** Type guard indicating failure */
48 public isErr(): this is Err<T, E> {
49 return true;
50 }
51
52 public unwrap(): T {
53 throw this.error;
54 }
55}
56
57/**
58 * Represents the outcome of an operation that can either succeed (Ok) or fail (Err).
59 * @template T The type of the value in case of success.
60 * @template E The type of the error in case of failure. Defaults to `Error`.
61 */
62export type Result<T, E = Error> = Ok<T, E> | Err<T, E>;
63
64// Static factory functions
65
66/**
67 * Creates an Ok result containing the success value.
68 * @param value The success value.
69 */
70export const ok = <T, E>(value: T): Ok<T, E> => new Ok(value);
71
72/**
73 * Creates an Err result containing the error value.
74 * @param error The error value.
75 */
76export const err = <T, E>(error: E): Err<T, E> => new Err(error);
77
78/**
79 * Combines multiple Result instances. If all are Ok, returns an Ok result with an array of values.
80 * If any Result is an Err, returns the first Err encountered.
81 * Note: This basic version assumes all results have the same error type E.
82 * And collects values into an array T[].
83 * @param results An array of Result instances.
84 * @returns A single Result. Ok<T[], E> if all succeed, otherwise the first Err<any, E>.
85 */
86export const combine = <T, E>(results: Result<T, E>[]): Result<T[], E> => {
87 const values: T[] = [];
88 for (const result of results) {
89 if (result.isErr()) {
90 // If we find an error, return it immediately.
91 // We cast the error result to Err<T[], E> for type compatibility.
92 // The T[] part doesn't matter since it's an error state.
93 return err<T[], E>(result.error);
94 }
95 // If it's Ok, collect the value
96 values.push(result.value);
97 }
98 // If no errors were found, return an Ok with the collected values
99 return ok<T[], E>(values);
100};
101
102// --- Either Type (kept separate for now) ---
103
104export type Either<L, A> = Left<L, A> | Right<L, A>;
105
106export class Left<L, A> {
107 readonly value: L;
108
109 constructor(value: L) {
110 this.value = value;
111 }
112
113 isLeft(): this is Left<L, A> {
114 return true;
115 }
116
117 isRight(): this is Right<L, A> {
118 return false;
119 }
120}
121
122export class Right<L, A> {
123 readonly value: A;
124
125 constructor(value: A) {
126 this.value = value;
127 }
128
129 isLeft(): this is Left<L, A> {
130 return false;
131 }
132
133 isRight(): this is Right<L, A> {
134 return true;
135 }
136}
137
138export const left = <L, A>(l: L): Either<L, A> => {
139 return new Left(l);
140};
141
142export const right = <L, A>(a: A): Either<L, A> => {
143 return new Right<L, A>(a);
144};