Uecko_ERP/packages/rdx-utils/src/helpers/result.ts
2025-05-09 12:45:32 +02:00

93 lines
2.4 KiB
TypeScript

export class Result<T, E extends Error = Error> {
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<T>(data?: T): Result<T, never> {
return new Result<T, never>({ isSuccess: true, data });
}
static fail<E extends Error = Error>(error?: E): Result<never, E> {
return new Result<never, E>({ isSuccess: false, error });
}
static combine(results: Result<any, any>[]): Result<any, any> {
for (const result of results) {
if (result.isFailure) {
return result;
}
}
return Result.ok<any>();
}
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<U>(fn: (value: T) => U): Result<U, Error> {
if (this.isSuccess && this._data !== undefined) {
return Result.ok<U>(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<R>(onOk: (data: T) => R, onError: (error: E) => R): R`
* Evalúa el `Result`: ejecuta `onOk()` si es `ok` o `onError()` si es `fail`.
*/
match<R>(onOk: (data: T) => R, onError: (error: E) => R): R {
return this._isSuccess ? onOk(this.data) : onError(this.error);
}
}