separate types

This commit is contained in:
Oli Sturm
2026-04-24 18:15:22 +01:00
parent b1ac745d70
commit d0c9466170
13 changed files with 178 additions and 221 deletions
+19
View File
@@ -0,0 +1,19 @@
export declare const logReturn: <T>(message: string, r: T) => T;
export declare const output: <T>(src: string, message: string) => (x: T) => void;
export declare const outputWith: <T>(
src: string,
renderText: (x: T) => string
) => (x: T) => void;
export declare const outputError: <E>(src: string, error: E) => void;
export declare const log: <T, E>(src: string, message: string) => (
result: import('./result.js').Result<T, E>
) => import('./result.js').Result<T, E>;
export declare const logWith: <T, E>(
src: string,
renderText: (x: T) => string
) => (result: import('./result.js').Result<T, E>) => import('./result.js').Result<T, E>;
-35
View File
@@ -1,59 +1,24 @@
import { tap, tapError } from './result.js';
/**
* @template T
* @param {string} message
* @param {T} r
* @returns {T}
*/
export const logReturn = (message, r) => {
console.log(message);
return r;
};
/**
* @template T
* @param {string} src
* @param {string} message
* @returns {(x: T) => void}
*/
export const output = (src, message) => (x) => {
const value = typeof x === 'object' ? JSON.stringify(x) : x;
console.log(`[${src}] ${message} | ${value}`);
};
/**
* @template T
* @param {string} src
* @param {(x: T) => string} renderText
* @returns {(x: T) => void}
*/
export const outputWith = (src, renderText) => (x) => {
console.log(`[${src}] ${renderText(x)}`);
};
/**
* @template E
* @param {string} src
* @param {E} error
*/
export const outputError = (src, error) => {
const value = typeof error === 'object' ? JSON.stringify(error) : error;
console.error(`\x1b[1;31m[${src} ERROR]\x1b[0m ${value}`);
};
/**
* @template T, E
* @param {string} src
* @param {string} message
* @returns {(result: import('./result.js').Result<T, E>) => import('./result.js').Result<T, E>}
*/
export const log = (src, message) => tap(output(src, message));
/**
* @template T, E
* @param {string} src
* @param {(x: T) => string} renderText
* @returns {(result: import('./result.js').Result<T, E>) => import('./result.js').Result<T, E>}
*/
export const logWith = (src, renderText) => tap(outputWith(src, renderText));
+57
View File
@@ -0,0 +1,57 @@
export type Success<T> = {
isSuccess: true;
isFailure: false;
value: T;
};
export type Failure<E> = {
isSuccess: false;
isFailure: true;
error: E;
};
export type Result<T, E> = Success<T> | Failure<E>;
export declare const ok: <T, E>(value: T) => Result<T, E>;
export declare const fail: <T, E>(error: E) => Result<T, E>;
export declare const catchResult: <T, E>(
f: () => T,
exceptionMapper: (err: Error) => E
) => Result<T, E>;
export declare const bind: <TIn, TOut, E>(
binder: (value: TIn) => Result<TOut, E>
) => (result: Result<TIn, E>) => Result<TOut, E>;
export declare const map: <TIn, TOut, E>(
mapper: (value: TIn) => TOut
) => (result: Result<TIn, E>) => Result<TOut, E>;
export declare const tap: <T, E>(
action: (value: T) => void
) => (result: Result<T, E>) => Result<T, E>;
export declare const tapError: <T, E>(
action: (error: E) => void
) => (result: Result<T, E>) => Result<T, E>;
export declare const mapError: <T, EIn, EOut>(
mapper: (error: EIn) => EOut
) => (result: Result<T, EIn>) => Result<T, EOut>;
export declare const match: <T, E, TResult>(
onSuccess: (value: T) => TResult,
onFailure: (error: E) => TResult
) => (result: Result<T, E>) => TResult;
export declare const switch_: <T, E>(
onSuccess: (value: T) => void,
onFailure: (error: E) => void
) => (result: Result<T, E>) => void;
export declare const pipe: <T, E>(
result: Result<T, E>,
...fns: Array<(arg: Result<any, any>) => Result<any, any>>
) => Result<any, any>;
-70
View File
@@ -1,40 +1,15 @@
/**
* @template T, E
* @typedef {Object} Result
* @property {boolean} isSuccess
* @property {boolean} isFailure
* @property {T} [value] - Present when isSuccess is true
* @property {E} [error] - Present when isFailure is true
*/
/**
* @template T, E
* @param {T} value
* @returns {Result<T, E>}
*/
export const ok = (value) => ({
isSuccess: true,
isFailure: false,
value,
});
/**
* @template T, E
* @param {E} error
* @returns {Result<T, E>}
*/
export const fail = (error) => ({
isSuccess: false,
isFailure: true,
error,
});
/**
* @template T, E
* @param {() => T} f
* @param {(err: Error) => E} exceptionMapper
* @returns {Result<T, E>}
*/
export const catchResult = (f, exceptionMapper) => {
try {
return ok(f());
@@ -43,77 +18,32 @@ export const catchResult = (f, exceptionMapper) => {
}
};
/**
* @template TIn, TOut, E
* @param {(value: TIn) => Result<TOut, E>} binder
* @returns {(result: Result<TIn, E>) => Result<TOut, E>}
*/
export const bind = (binder) => (result) =>
result.isSuccess ? binder(result.value) : fail(result.error);
/**
* @template TIn, TOut, E
* @param {(value: TIn) => TOut} mapper
* @returns {(result: Result<TIn, E>) => Result<TOut, E>}
*/
export const map = (mapper) => (result) =>
result.isSuccess ? ok(mapper(result.value)) : fail(result.error);
/**
* @template T, E
* @param {(value: T) => void} action
* @returns {(result: Result<T, E>) => Result<T, E>}
*/
export const tap = (action) => (result) => {
if (result.isSuccess) action(result.value);
return result;
};
/**
* @template T, E
* @param {(error: E) => void} action
* @returns {(result: Result<T, E>) => Result<T, E>}
*/
export const tapError = (action) => (result) => {
if (result.isFailure) action(result.error);
return result;
};
/**
* @template T, EIn, EOut
* @param {(error: EIn) => EOut} mapper
* @returns {(result: Result<T, EIn>) => Result<T, EOut>}
*/
export const mapError = (mapper) => (result) =>
result.isSuccess ? ok(result.value) : fail(mapper(result.error));
/**
* @template T, E, TResult
* @param {(value: T) => TResult} onSuccess
* @param {(error: E) => TResult} onFailure
* @returns {(result: Result<T, E>) => TResult}
*/
export const match = (onSuccess, onFailure) => (result) =>
result.isSuccess ? onSuccess(result.value) : onFailure(result.error);
/**
* @template T, E
* @param {(value: T) => void} onSuccess
* @param {(error: E) => void} onFailure
* @returns {(result: Result<T, E>) => void}
*/
export const switch_ = (onSuccess, onFailure) => (result) => {
if (result.isSuccess) onSuccess(result.value);
else onFailure(result.error);
};
/**
* Pipe a result through a series of functions (left-to-right composition).
* This allows chaining without extending the prototype.
* @template T, E
* @param {Result<T, E>} result
* @param {...Function} fns
* @returns {Result<any, any>}
*/
export const pipe = (result, ...fns) =>
fns.reduce((acc, fn) => fn(acc), result);