ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 10.7 kB
JavaScript
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