export class Result { private readonly isSuccess: boolean; private readonly _data?: T; private readonly _error?: E; private constructor(props: { isSuccess: boolean; error?: E; data?: T }) { const { isSuccess, error, data } = props; if (isSuccess && error) { throw new Error(`InvalidOperation: A result cannot be successful and contain an error`); } if (!isSuccess && !error) { throw new Error(`InvalidOperation: A failing result needs to contain an error message`); } this.isSuccess = isSuccess; this._error = error; this._data = data; Object.freeze(this); } static ok(data?: T): Result { return new Result({ isSuccess: true, data }); } static fail(error?: E): Result { return new Result({ isSuccess: false, error }); } static combine(results: Result[]): Result { for (const result of results) { if (result.isError()) { return result; } } return Result.ok(); } isOk(): boolean { return this.isSuccess; } isError(): boolean { return !this.isSuccess; } get data(): T { if (!this.isSuccess) { throw new Error("Cannot get value data from a failed result."); } return this._data as T; } get error(): E { if (this.isSuccess) { throw new Error("Cannot get error from a successful result."); } return this._error as E; } /** * 🔹 `getOrElse(defaultValue: T): T` * Si el `Result` es un `ok`, devuelve `data`, de lo contrario, devuelve `defaultValue`. */ getOrElse(defaultValue: any): T | any { return this.isSuccess ? this.data : defaultValue; } /** * 🔹 `match(onOk: (data: T) => R, onError: (error: E) => R): R` * Evalúa el `Result`: ejecuta `onOk()` si es `ok` o `onError()` si es `fail`. */ match(onOk: (data: T) => R, onError: (error: E) => R): R { return this.isSuccess ? onOk(this.data) : onError(this.error); } }