"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeSafetyUtils = void 0;
const lodash_1 = require("lodash");
const _ = require("lodash");
class TypeSafetyUtils {
    /** Ensures a type-safe boolean tri-state value `true|false|undefined`,
     * using the `toBoolean()` method on this class.
     *
     * For defined values, performs a hard data conversion to a boolean (not just a cast or a Boolean() call).
     *
     * Converts strings to lowercase while determining and throws if value is anything other than null, undefined, or a valid boolean string ("true", "false", "yes", "no", "0", "1").
     *
     * *This method reflects one of the myriad idiosyncracies of typescript: in this case that there's
     * no reliable built-in converter function (like a `boolean.tryParse()`) for booleans,
     * and we have to waste time and performance rolling and importing and calling our own #$!*$%!@^!!*     */
    static toTriState(value) {
        return (value == undefined) ? undefined : this.toBoolean(value);
    }
    /** Performs a hard data conversion to a boolean (not just a cast or a Boolean() call).
     *
     * Converts strings to lowercase while determining and throws if value is not a valid boolean string ("true", "false", "yes", "no", "0", "1").
     *
     * *This method reflects one of the myriad idiosyncracies of typescript: in this case that there's
     * no reliable built-in converter function (like a `boolean.tryParse()`) for booleans,
     * and we have to waste time and performance rolling and importing and calling our own #$!*$%!@^!!*     */
    static toBoolean(value) {
        if (value == undefined || value == null) {
            return false;
        }
        else if (typeof value == "boolean") {
            return Boolean(value);
        }
        else if ((0, lodash_1.isNumber)(value)) {
            return Boolean(value != 0);
        }
        else if (typeof value == 'string') {
            if (value.trim() == "") {
                return false; // empty or whitespace
            }
            else {
                const result = TypeSafetyUtils.VALID_BOOLEAN_STRINGS.get(value.toLowerCase());
                if (result !== undefined) {
                    return result;
                }
                else {
                    throw new Error('Invalid boolean string');
                }
            }
        }
        else {
            return Boolean(value); // <-- this is a grab bag.  Who knows what we'll get.
        }
    }
    /** Determines whether the given string is a valid member of the given enum definition. */
    static isEnum(enumDef, entry) {
        return ((entry != undefined) && (Object.values(enumDef).indexOf(entry) >= 0));
    }
    /** Converts to the given enum value based on comparison of enum VALUE (NOT enum property name).
     * Returns undefined when not recognized.
     *
     * DEVNOTE:  Replaces hasty syntax:
     * ```
     * const asEnum = (str) ? (<any>MyEnum)[str] : undefined;
     * ```
     */
    static toEnum(enumDef, entry) {
        if (entry === undefined)
            return undefined;
        let output;
        return Object.values(enumDef).includes(entry) ? entry : undefined;
    }
    /** Converts to the given enum value based on comparison of enum VALUE (NOT enum property name).
     * Returns undefined when not recognized.
     *
     * DEVNOTE:  Replaces hasty syntax:
     * ```
     * const asEnum = (str) ? (<any>MyEnum)[str] : undefined;
     * ```
     */
    static toEnum_ts(enumObj, str) {
        if (str === undefined)
            return undefined;
        return Object.values(enumObj).includes(str) ? str : undefined;
    }
    /** (Mutates the given subject) Removes any properties that are set to undefined. */
    static pruneUndefined(subject) {
        for (let key of Object.keys(subject)) {
            if (subject[key] == undefined)
                delete subject[key];
        }
        //  ...OR... 
        // for (let [key, value] of Object.entries(subject)) {
        //   if (value == undefined) delete (<any>subject)[key] 
        // };
        // ...OR...
        // return _.omitBy(subject, _.isUndefined) as T;
        return subject;
    }
    /** Folds the explicits over the defaults into a new object.
     * Does NOT mutate either input object.
     *
     * @param overlayWithUndefined (optional, defaults to false) when set to true, any property in the explicit object with an undefined value will overwrite its counterpart in the given defaults object.  That is, unless this flag is set to true, this method will ignore any properties on the explicit object that are explicitely set to `undefined`.
     * */
    static overlay(defaults, explicits, overlayWithUndefined = false) {
        if (overlayWithUndefined) {
            return _.assignIn({}, defaults, explicits);
        }
        else {
            return _.assignIn({}, defaults, explicits ? TypeSafetyUtils.pruneUndefined(explicits) : undefined);
        }
    }
}
exports.TypeSafetyUtils = TypeSafetyUtils;
TypeSafetyUtils.VALID_BOOLEAN_STRINGS = new Map([
    ['true', true],
    ['false', false],
    ['yes', true],
    ['no', false],
    ['1', true],
    ['0', false]
]);
//# sourceMappingURL=type-safety-utils.js.map