ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 21.4 kB
JavaScript
import { isRecord } from '../guard/is-record.mjs';
import { unknownToString } from '../others/unknown-to-string.mjs';
import { Optional } from './optional.mjs';
/** @internal String literal tag to identify the 'Ok' variant of Result. */
const OkTypeTagName = 'ts-data-forge::Result.ok';
/** @internal String literal tag to identify the 'Err' variant of Result. */
const ErrTypeTagName = 'ts-data-forge::Result.err';
/**
* Namespace for the `Result` type and related functions.
* Provides utilities to handle operations that can succeed or fail.
*/
var Result;
(function (Result) {
/**
* Checks if the given value is a `Result`.
* @param maybeOptional The value to check.
* @returns `true` if the value is a `Result`, otherwise `false`.
*/
Result.isResult = (maybeOptional) => isRecord(maybeOptional) &&
Object.hasOwn(maybeOptional, '$$tag') &&
Object.hasOwn(maybeOptional, 'value') &&
(maybeOptional['$$tag'] === ErrTypeTagName ||
maybeOptional['$$tag'] === OkTypeTagName);
/**
* Creates a `Result.Ok` containing the given success value.
*
* Use this constructor when an operation succeeds and you want to wrap
* the successful result in a Result type for consistent error handling.
*
* @template S The type of the success value.
* @param value The success value.
* @returns A `Result.Ok<S>` containing the value.
* @example
* ```typescript
* // Basic success case
* const success = Result.ok(42);
* console.log(Result.isOk(success)); // true
* console.log(Result.unwrapOk(success)); // 42
*
* // Function that returns a Result
* function divide(a: number, b: number): Result<number, string> {
* if (b === 0) {
* return Result.err("Division by zero");
* }
* return Result.ok(a / b);
* }
*
* const result = divide(10, 2);
* console.log(Result.unwrapOk(result)); // 5
* ```
*/
Result.ok = (value) => ({
$$tag: OkTypeTagName,
value,
});
/**
* Creates a `Result.Err` containing the given error value.
*
* Use this constructor when an operation fails and you want to wrap
* the error information in a Result type for consistent error handling.
*
* @template E The type of the error value.
* @param value The error value.
* @returns A `Result.Err<E>` containing the value.
* @example
* ```typescript
* // Basic error case
* const failure = Result.err("Something went wrong");
* console.log(Result.isErr(failure)); // true
* console.log(Result.unwrapErr(failure)); // "Something went wrong"
*
* // Function that can fail
* function parseInteger(input: string): Result<number, string> {
* const num = parseInt(input, 10);
* if (isNaN(num)) {
* return Result.err(`Invalid number format: ${input}`);
* }
* return Result.ok(num);
* }
*
* const result = parseInteger("abc");
* console.log(Result.unwrapErr(result)); // "Invalid number format: abc"
*
* // Using custom error types
* interface ValidationError {
* field: string;
* message: string;
* }
*
* const validationError = Result.err<ValidationError>({
* field: "email",
* message: "Invalid email format"
* });
* ```
*/
Result.err = (value) => ({
$$tag: ErrTypeTagName,
value,
});
/**
* @internal
* Default string conversion function using native String constructor.
*/
const toStr_ = String;
/**
* Checks if a `Result` is `Result.Ok`.
* Acts as a type guard, narrowing the type to the success variant.
*
* This function is essential for type-safe Result handling, allowing
* TypeScript to understand that subsequent operations will work with
* the success value rather than the error value.
*
* @template R The `Result.Base` type to check.
* @param result The `Result` to check.
* @returns `true` if the `Result` is `Result.Ok`, otherwise `false`.
* @example
* ```typescript
* // Basic type guard usage
* const result: Result<number, string> = divide(10, 2);
*
* if (Result.isOk(result)) {
* // TypeScript knows result is Result.Ok<number>
* console.log(result.value); // Safe to access .value
* console.log(Result.unwrapOk(result)); // 5
* } else {
* // TypeScript knows result is Result.Err<string>
* console.log(result.value); // Error message
* }
*
* // Using in conditional logic
* const processResult = (r: Result<string, Error>) => {
* return Result.isOk(r)
* ? r.value.toUpperCase() // Safe string operations
* : "Error occurred";
* };
*
* // Filtering arrays of Results
* const results: Result<number, string>[] = [
* Result.ok(1),
* Result.err("error"),
* Result.ok(2)
* ];
* const successes = results.filter(Result.isOk);
* // successes is Result.Ok<number>[]
* ```
*/
Result.isOk = (result) => result.$$tag === OkTypeTagName;
/**
* Checks if a `Result` is `Result.Err`.
* Acts as a type guard, narrowing the type to the error variant.
*
* This function is essential for type-safe Result handling, allowing
* TypeScript to understand that subsequent operations will work with
* the error value rather than the success value.
*
* @template R The `Result.Base` type to check.
* @param result The `Result` to check.
* @returns `true` if the `Result` is `Result.Err`, otherwise `false`.
* @example
* ```typescript
* // Basic type guard usage
* const result: Result<number, string> = divide(10, 0);
*
* if (Result.isErr(result)) {
* // TypeScript knows result is Result.Err<string>
* console.log(result.value); // Safe to access error .value
* console.log(Result.unwrapErr(result)); // "Division by zero"
* } else {
* // TypeScript knows result is Result.Ok<number>
* console.log(result.value); // Success value
* }
*
* // Error handling patterns
* const handleResult = (r: Result<Data, ApiError>) => {
* if (Result.isErr(r)) {
* logError(r.value); // Safe error operations
* return null;
* }
* return processData(r.value);
* };
*
* // Collecting errors from multiple Results
* const results: Result<string, ValidationError>[] = validateForm();
* const errors = results
* .filter(Result.isErr)
* .map(err => err.value); // ValidationError[]
* ```
*/
Result.isErr = (result) => result.$$tag === ErrTypeTagName;
/**
* Unwraps a `Result`, returning the success value.
* Throws an error if the `Result` is `Result.Err`.
*
* This is useful when you're confident that a Result should contain a success value
* and want to treat errors as exceptional conditions. The error message will be
* constructed from the error value using the provided string conversion function.
*
* @template R The `Result.Base` type to unwrap.
* @param result The `Result` to unwrap.
* @param toStr An optional function to convert the error value to a string for the error message. Defaults to `String`.
* @returns The success value if `Result.Ok`.
* @throws {Error} Error with the stringified error value if the `Result` is `Result.Err`.
* @example
* ```typescript
* const success = Result.ok(42);
* console.log(Result.unwrapThrow(success)); // 42
*
* const failure = Result.err("Network error");
* try {
* Result.unwrapThrow(failure); // throws Error: "Network error"
* } catch (error) {
* console.log(error.message); // "Network error"
* }
* ```
*/
Result.unwrapThrow = (result, toStr = toStr_) => {
if (Result.isErr(result)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
throw new Error(toStr(result.value));
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return result.value;
};
function unwrapOk(result) {
return Result.isErr(result)
? undefined
: // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
result.value;
}
Result.unwrapOk = unwrapOk;
function unwrapOkOr(...args) {
switch (args.length) {
case 2: {
// Direct version: first argument is result
const [result, defaultValue] = args;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return Result.isErr(result) ? defaultValue : result.value;
}
case 1: {
// Curried version: first argument is default value
const [defaultValue] = args;
return (result) => unwrapOkOr(result, defaultValue);
}
}
}
Result.unwrapOkOr = unwrapOkOr;
/**
* Unwraps a `Result`, returning the error value.
* Throws an error if the `Result` is `Result.Ok`.
*
* This function is used when you expect a Result to be an error and want to
* extract the error value. If the Result is unexpectedly Ok, it will throw
* an error with information about the unexpected success value.
*
* @template R The `Result.Base` type to unwrap.
* @param result The `Result` to unwrap.
* @param toStr An optional function to convert the success value to a string for the error message when the Result is unexpectedly Ok. Defaults to `String`.
* @returns The error value if `Result.Err`.
* @throws {Error} Error with message "Expected Err but got Ok: {value}" if the `Result` is `Result.Ok`.
* @example
* ```typescript
* const failure = Result.err("Network timeout");
* console.log(Result.unwrapErrThrow(failure)); // "Network timeout"
*
* const success = Result.ok(42);
* try {
* Result.unwrapErrThrow(success); // throws Error: "Expected Err but got Ok: 42"
* } catch (error) {
* console.log(error.message); // "Expected Err but got Ok: 42"
* }
* ```
*/
Result.unwrapErrThrow = (result, toStr = toStr_) => {
if (Result.isOk(result)) {
throw new Error(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
`Expected Err but got Ok: ${toStr(result.value)}`);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return result.value;
};
/**
* Unwraps a `Result`, returning the error value or `undefined` if it is `Result.Ok`.
*
* This provides a safe way to extract error values from Results without throwing
* exceptions. Useful for error handling patterns where you want to check for
* specific error conditions.
*
* @template R The `Result.Base` type to unwrap.
* @param result The `Result` to unwrap.
* @returns The error value if `Result.Err`, otherwise `undefined`.
* @example
* ```typescript
* const failure = Result.err("Connection failed");
* console.log(Result.unwrapErr(failure)); // "Connection failed"
*
* const success = Result.ok(42);
* console.log(Result.unwrapErr(success)); // undefined
* ```
*/
Result.unwrapErr = (result) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Result.isErr(result) ? result.value : undefined;
function unwrapErrOr(...args) {
switch (args.length) {
case 2: {
// Direct version: first argument is result
const [result, defaultValue] = args;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return Result.isErr(result) ? result.value : defaultValue;
}
case 1: {
// Curried version: first argument is default value
const [defaultValue] = args;
return (result) => unwrapErrOr(result, defaultValue);
}
}
}
Result.unwrapErrOr = unwrapErrOr;
function map(...args) {
switch (args.length) {
case 2: {
const [result, mapFn] = args;
return Result.isErr(result)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
result
: // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Result.ok(mapFn(result.value));
}
case 1: {
// Curried version: first argument is mapping function
const [mapFn] = args;
return (result) => map(result, mapFn);
}
}
}
Result.map = map;
function mapErr(...args) {
switch (args.length) {
case 2: {
const [result, mapFn] = args;
return Result.isOk(result)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
result
: // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Result.err(mapFn(result.value));
}
case 1: {
// Curried version: first argument is mapping function
const [mapFn] = args;
return (result) => mapErr(result, mapFn);
}
}
}
Result.mapErr = mapErr;
function fold(...args) {
switch (args.length) {
case 3: {
const [result, mapFn, mapErrFn] = args;
return Result.isOk(result)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Result.ok(mapFn(result.value))
: // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Result.err(mapErrFn(result.value));
}
case 2: {
const [mapFn, mapErrFn] = args;
return (result) => Result.isOk(result) ? Result.ok(mapFn(result.value)) : Result.err(mapErrFn(result.value));
}
}
}
Result.fold = fold;
function flatMap(...args) {
switch (args.length) {
case 2: {
const [result, flatMapFn] = args;
return Result.isErr(result)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
result
: // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
flatMapFn(result.value);
}
case 1: {
const [flatMapFn] = args;
return (result) => Result.isErr(result) ? result : flatMapFn(result.value);
}
}
}
Result.flatMap = flatMap;
function expectToBe(...args) {
switch (args.length) {
case 2: {
// Direct version: first argument is result
const [result, message] = args;
if (Result.isOk(result)) {
return unwrapOk(result);
}
throw new Error(message);
}
case 1: {
// Curried version: first argument is message
const [message] = args;
return (result) => expectToBe(result, message);
}
}
}
Result.expectToBe = expectToBe;
/**
* Converts a Promise into a Promise that resolves to a `Result`.
* If the input Promise resolves, the `Result` will be `Ok` with the resolved value.
* If the input Promise rejects, the `Result` will be `Err` with the rejection reason.
* @template P The type of the input Promise.
* @param promise The Promise to convert.
* @returns A Promise that resolves to `Result<UnwrapPromise<P>, unknown>`.
*/
Result.fromPromise = (promise) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
promise.then((v) => Result.ok(v)).catch(Result.err);
/**
* Wraps a function that may throw an exception in a `Result`.
*
* This is a fundamental utility for converting traditional exception-based error
* handling into Result-based error handling. Any thrown value is converted to an
* Error object for consistent error handling.
*
* If the function executes successfully, returns `Result.Ok` with the result.
* If the function throws, returns `Result.Err` with the caught error.
*
* @template T The return type of the function.
* @param fn The function to execute that may throw.
* @returns A `Result<T, Error>` containing either the successful result or the caught error.
* @example
* ```typescript
* // Wrapping JSON.parse which can throw
* const parseJson = <T>(text: string): Result<T, Error> =>
* Result.fromThrowable(() => JSON.parse(text) as T);
*
* const validJson = parseJson<{valid: boolean}>('{"valid": true}');
* if (Result.isOk(validJson)) {
* console.log(validJson.value.valid); // true
* }
*
* ```
*/
Result.fromThrowable = (fn) => {
try {
return Result.ok(fn());
}
catch (error) {
if (error instanceof Error) {
return Result.err(error);
}
const msg = unknownToString(error);
return Result.err(new Error(msg));
}
};
/**
* Swaps the success and error values of a `Result`.
* @template R The input `Result.Base` type.
* @param result The `Result` to swap.
* @returns A new `Result` with success and error swapped.
* @example
* ```typescript
* const okResult = Result.ok(42);
* const swapped = Result.swap(okResult);
* console.log(Result.isErr(swapped)); // true
* console.log(Result.unwrapErr(swapped)); // 42
* ```
*/
Result.swap = (result) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Result.isOk(result) ? Result.err(unwrapOk(result)) : Result.ok(result.value);
/**
* Converts a `Result` to an `Optional`.
*
* This conversion is useful when you want to discard error information and only
* care about whether an operation succeeded. The error information is lost in
* this conversion, so use it when error details are not needed.
*
* If the `Result` is `Ok`, returns `Some` with the value.
* If the `Result` is `Err`, returns `None`.
*
* @template R The input `Result.Base` type.
* @param result The `Result` to convert.
* @returns An `Optional<UnwrapOk<R>>` containing the success value or representing `None`.
* @example
* ```typescript
* // Basic conversion
* const okResult = Result.ok(42);
* const optional = Result.toOptional(okResult);
* console.log(Optional.isSome(optional)); // true
* console.log(Optional.unwrap(optional)); // 42
*
* const errResult = Result.err("Network error");
* const none = Result.toOptional(errResult);
* console.log(Optional.isNone(none)); // true
*
* ```
*/
Result.toOptional = (result) => Result.isOk(result) ? Optional.some(unwrapOk(result)) : Optional.none;
function orElse(...args) {
switch (args.length) {
case 2: {
const [result, alternative] = args;
return Result.isOk(result) ? result : alternative;
}
case 1: {
// Curried version: one argument (alternative) provided
const [alternative] = args;
return (result) => orElse(result, alternative);
}
}
}
Result.orElse = orElse;
/**
* Combines two `Result` values into a single `Result` containing a tuple.
* If either `Result` is `Err`, returns the first `Err` encountered.
* @template S1 The success type of the first `Result`.
* @template E1 The error type of the first `Result`.
* @template S2 The success type of the second `Result`.
* @template E2 The error type of the second `Result`.
* @param resultA The first `Result`.
* @param resultB The second `Result`.
* @returns A `Result` containing a tuple of both values, or the first `Err`.
* @example
* ```typescript
* const a = Result.ok(1);
* const b = Result.ok("hello");
* const zipped = Result.zip(a, b);
* console.log(Result.unwrapOk(zipped)); // [1, "hello"]
*
* const withErr = Result.zip(a, Result.err("error"));
* console.log(Result.unwrapErr(withErr)); // "error"
* ```
*/
Result.zip = (resultA, resultB) => Result.isOk(resultA)
? Result.isOk(resultB)
? Result.ok([resultA.value, resultB.value])
: resultB
: resultA;
})(Result || (Result = {}));
export { Result };
//# sourceMappingURL=result.mjs.map