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.isFailure) { return result; } } return Result.ok(); } get isSuccess(): boolean { return this._isSuccess; } get isFailure(): boolean { return !this._isSuccess; } get data(): T { return this.getData(); } get error(): E { return this.getError(); } getError(): E { if (this._isSuccess) { throw new Error("Cannot get error from a successful result."); } return this._error as E; } getData(): T { if (!this._isSuccess) { throw new Error("Cannot get value data from a failed result."); } return this._data as T; } map(fn: (value: T) => U): Result { if (this.isSuccess && this._data !== undefined) { return Result.ok(fn(this.data)); } return Result.fail(this.error || new Error("Unknown error")); } /** * 🔹 `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); } }