UNPKG

ts-data-forge

Version:

[![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![License](https://img.shields.

269 lines (266 loc) 10.7 kB
import { isRecord } from '../guard/is-record.mjs'; /** @internal String literal tag to identify the 'Some' variant of Optional. */ const SomeTypeTagName = 'ts-data-forge::Optional.some'; /** @internal String literal tag to identify the 'None' variant of Optional. */ const NoneTypeTagName = 'ts-data-forge::Optional.none'; /** * Namespace for the {@link Optional} type and related functions. * Provides utilities to handle values that might be absent, similar to Option types in other languages. */ var Optional; (function (Optional) { /** * Checks if the given value is an {@link Optional}. * @param maybeOptional The value to check. * @returns `true` if the value is an {@link Optional}, otherwise `false`. */ Optional.isOptional = (maybeOptional) => isRecord(maybeOptional) && Object.hasOwn(maybeOptional, '$$tag') && ((maybeOptional['$$tag'] === SomeTypeTagName && Object.hasOwn(maybeOptional, 'value')) || maybeOptional['$$tag'] === NoneTypeTagName); /** * Creates an {@link Optional.Some} containing the given value. * @template S The type of the value. * @param value The value to wrap in an {@link Optional.Some}. * @returns An {@link Optional.Some}<S> containing the value. * @example * ```typescript * const someValue = Optional.some(42); * console.log(Optional.isSome(someValue)); // true * console.log(Optional.unwrap(someValue)); // 42 * ``` */ Optional.some = (value) => ({ $$tag: SomeTypeTagName, value, }); /** * The singleton instance representing {@link Optional.None} (an empty Optional). * @example * ```typescript * const emptyValue = Optional.none; * console.log(Optional.isNone(emptyValue)); // true * console.log(Optional.unwrapOr(emptyValue, "default")); // "default" * ``` */ Optional.none = { $$tag: NoneTypeTagName }; /** * Checks if an {@link Optional} is {@link Optional.Some}. * Acts as a type guard. * @template O The {@link Optional.Base} type to check. * @param optional The {@link Optional} to check. * @returns `true` if the {@link Optional} is {@link Optional.Some}, `false` otherwise. */ Optional.isSome = (optional) => optional.$$tag === SomeTypeTagName; /** * Checks if an {@link Optional} is {@link Optional.None}. * Acts as a type guard. * @template O The {@link Optional.Base} type to check. * @param optional The {@link Optional} to check. * @returns `true` if the {@link Optional} is {@link Optional.None}, `false` otherwise. */ Optional.isNone = (optional) => optional.$$tag === NoneTypeTagName; /** * Unwraps an `Optional`, returning the contained value. * Throws an error if the `Optional` is `Optional.None`. * * This is a safer alternative to direct value access when you know the Optional * should contain a value. Use this method when an empty Optional represents * a programming error or unexpected condition. * * @template O The `Optional.Base` type to unwrap. * @param optional The `Optional` to unwrap. * @returns The contained value if `Optional.Some`. * @throws {Error} Error with message "`unwrapThrow()` has failed because it is `None`" if the `Optional` is `Optional.None`. * @example * ```typescript * const userInput = Optional.some(42); * console.log(Optional.unwrapThrow(userInput)); // 42 * * const empty = Optional.none; * try { * Optional.unwrapThrow(empty); // throws Error * } catch (error) { * console.log(error.message); // "`unwrapThrow()` has failed because it is `None`" * } * ``` */ Optional.unwrapThrow = (optional) => { if (Optional.isSome(optional)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return optional.value; } throw new Error('`unwrapThrow()` has failed because it is `None`'); }; function unwrap(optional) { return Optional.isSome(optional) ? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion optional.value : undefined; } Optional.unwrap = unwrap; function unwrapOr(...args) { switch (args.length) { case 2: { const [optional, defaultValue] = args; return Optional.isSome(optional) ? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion optional.value : defaultValue; } case 1: { // Curried version: first argument is default value const [defaultValue] = args; return (optional) => unwrapOr(optional, defaultValue); } } } Optional.unwrapOr = unwrapOr; function orElse(...args) { switch (args.length) { case 2: { const [optional, alternative] = args; return Optional.isNone(optional) ? alternative : optional; } case 1: { const [alternative] = args; return (optional) => orElse(optional, alternative); } } } Optional.orElse = orElse; function map(...args) { switch (args.length) { case 2: { const [optional, mapFn] = args; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return Optional.isSome(optional) ? Optional.some(mapFn(unwrap(optional))) : Optional.none; } case 1: { // Curried version: first argument is mapping function const [mapFn] = args; return (optional) => map(optional, mapFn); } } } Optional.map = map; function flatMap(...args) { switch (args.length) { case 2: { const [optional, flatMapFn] = args; return Optional.isSome(optional) ? flatMapFn(unwrap(optional)) : Optional.none; } case 1: { const [flatMapFn] = args; return (optional) => flatMap(optional, flatMapFn); } } } Optional.flatMap = flatMap; function filter(...args) { switch (args.length) { case 2: { const [optional, predicate] = args; if (Optional.isSome(optional)) { const value = unwrap(optional); return predicate(value) ? Optional.some(value) : Optional.none; } // If the optional is None, return None return Optional.none; } case 1: { // Curried version: first argument is predicate function const [predicate] = args; return (optional) => filter(optional, predicate); } } } Optional.filter = filter; function expectToBe(...args) { switch (args.length) { case 2: { const [optional, message] = args; if (Optional.isSome(optional)) { return unwrap(optional); } throw new Error(message); } case 1: { // Curried version: first argument is message const [message] = args; return (optional) => expectToBe(optional, message); } } } Optional.expectToBe = expectToBe; /** * Combines two `Optional` values into a single `Optional` containing a tuple. * If either `Optional` is `None`, returns `None`. * @template A The value type of the first `Optional`. * @template B The value type of the second `Optional`. * @param optionalA The first `Optional`. * @param optionalB The second `Optional`. * @returns An `Optional` containing a tuple of both values, or `None`. * @example * ```typescript * const a = Optional.some(1); * const b = Optional.some("hello"); * const zipped = Optional.zip(a, b); * console.log(Optional.unwrap(zipped)); // [1, "hello"] * * const withNone = Optional.zip(a, Optional.none); * console.log(Optional.isNone(withNone)); // true * ``` */ Optional.zip = (optionalA, optionalB) => Optional.isSome(optionalA) && Optional.isSome(optionalB) ? Optional.some([optionalA.value, optionalB.value]) : Optional.none; /** * Converts a nullable value to an `Optional`. * * This is the primary way to lift nullable values (null or undefined) into * the Optional type system. The function treats both `null` and `undefined` * as empty values, converting them to `Optional.None`. * * @template T The type of the nullable value. * @param value The nullable value to convert. * @returns `Optional.Some<NonNullable<T>>` if the value is not null or undefined, otherwise `Optional.None`. * @example * ```typescript * const value: string | null = "hello"; * const optional = Optional.fromNullable(value); * console.log(Optional.unwrap(optional)); // "hello" * * const nullValue: string | null = null; * const noneOptional = Optional.fromNullable(nullValue); * console.log(Optional.isNone(noneOptional)); // true * ``` */ Optional.fromNullable = (value) => (value == null ? Optional.none : Optional.some(value)); /** * Converts an `Optional` to a nullable value. * * This function extracts the value from an Optional, returning `undefined` * for empty Optionals. This is useful when interfacing with APIs or systems * that expect nullable values rather than Optional types. * * Note: This returns `undefined` (not `null`) for consistency with JavaScript's * undefined semantics and TypeScript's optional properties. * * @template O The `Optional.Base` type to convert. * @param optional The `Optional` to convert. * @returns The contained value if `Some`, otherwise `undefined`. * @example * ```typescript * const some = Optional.some(42); * console.log(Optional.toNullable(some)); // 42 * * const none = Optional.none; * console.log(Optional.toNullable(none)); // undefined * ``` */ Optional.toNullable = (optional) => (Optional.isSome(optional) ? unwrap(optional) : undefined); })(Optional || (Optional = {})); export { Optional }; //# sourceMappingURL=optional.mjs.map