type/conv.ts

/**
 * Type tools.
 *
 * @module type
 * @license Apache-2.0
 * @copyright Mat. 2018-present
 */

/* eslint-disable @typescript-eslint/prefer-function-type */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */

import type { NullToUndefined, UndefinedToNull } from "../type/utils";
import { curry } from "../func/curry";




/**
 * If `val` is `null` then return `undefined`, else return `val`.
 *
 * @function nullToUndefined
 * @param {T} val
 * @returns {NullToUndefined<T>} `val` is ensured not to be `null`
 */
export const nullToUndefined = <T>(val: T): NullToUndefined<T> =>
    (val === null ? undefined : val) as NullToUndefined<T>;




/**
 * If `val` is `undefined` then return `null`, else return `val`.
 *
 * @function undefinedToNull
 * @param {T} val
 * @returns {UndefinedToNull<T>} `val` is ensured not to be `undefined`
 */
export const undefinedToNull = <T>(val: T): UndefinedToNull<T> =>
    (val === undefined ? null : val) as UndefinedToNull<T>;




/**
 * Returns `false` for all **falsy** values
 * (`false`, `0`, `""`, `null`, `undefined`, and `NaN`),
 * and `true` for all **truthy** values.
 *
 * @function toBool
 * @param {unknown} x
 * @returns {Boolean}
 */
export const toBool = (x: unknown): boolean => !!x;




/**
 * Lazy nullish coalescing (inspired by `??` operator, but with
 * `right` operand eager evaluation prevented using function abstracion).
 *
 * Returns its `right` operand when its `left` operand
 * is `null` or `undefined`, and otherwise returns its `left` operand.
 *
 * In assignments works almost as `||` operator, but also properly handle
 * values coercing to `false` (0 or empty string).
 *
 * @function lazyNullishCoalesce
 * @param left
 * @param right
 * @returns left ?? right()
 */
export const lazyNullishCoalesce = curry(
    <L, R>(left: L, right: () => R) => left == null ? right() : left,
) as {
    /* uncurried */
    <L, R>(left: L, right: () => R):
        L extends null ? R : L extends undefined ? R : L;
    /* curried */
    <L>(left: L): {
        <R>(right: () => R):
            L extends null ? R : L extends undefined ? R : L;
    };
};