import { booleanFrom } from "./boolean.ts" import { isAnyFunction, isFunction } from "./is.ts" /** * @description Return the input value as is, ignoring any additional arguments. * * @example * ``` * // Expect: 5 * const example1 = asIs(5) * // Expect: "hello" * const example2 = asIs("hello", 1, 2, 3) * ``` */ export const asIs = (target: T, ..._args: unknown[]): T => target /** * @description Return undefined, ignoring any input arguments. * * @example * ``` * // Expect: undefined * const example1 = asUndefined() * // Expect: undefined * const example2 = asUndefined(1, "hello", null) * ``` */ export const asUndefined = (..._target: unknown[]): undefined => undefined /** * @description Return null, ignoring any input arguments. * * @example * ``` * // Expect: null * const example1 = asNull() * // Expect: null * const example2 = asNull(1, "hello", undefined) * ``` */ export const asNull = (..._target: unknown[]): null => null /** * @description Return void, ignoring any input arguments. * * @example * ``` * // Expect: void * const example1 = asVoid() * // Expect: void * const example2 = asVoid(1, "hello", null) * ``` */ export const asVoid = (..._target: unknown[]): void => { /* do nothing */ } /** * @description Check whether two values are strictly equal using `===`. * * @example * ``` * // Expect: true * const example1 = isStrictEqual(1, 1) * // Expect: false * const example2 = isStrictEqual(1, "1") * ``` * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness} */ export const isStrictEqual = (v1: unknown, v2: unknown): boolean => v1 === v2 /** * @description Check whether two values are loosely equal using `==`. * * @example * ``` * // Expect: true * const example1 = isLooseEqual(1, "1") * // Expect: false * const example2 = isLooseEqual(0, "1") * ``` * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness} */ // eslint-disable-next-line eqeqeq export const isLooseEqual = (v1: unknown, v2: unknown): boolean => v1 == v2 /** * @description Check whether two values are equal using `Object.is`. * * @example * ``` * // Expect: true * const example1 = isObjectEqual(NaN, NaN) * // Expect: false * const example2 = isObjectEqual(0, -0) * ``` * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness} * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is} */ export const isObjectEqual = (v1: unknown, v2: unknown): boolean => Object.is(v1, v2) /** * @description Invoke the conditional function with the target value, according to the predicate result. * * @example * ``` * const example1 = ifElse(true, () => "yes", () => "no") * // Expect: "yes" * const example2 = ifElse(false, () => "yes", () => "no") * // Expect: "no" * ``` */ export const ifElse = ( pred: unknown, thenFn: (x?: X) => TR, elseFn: (x?: X) => ER, x?: X, ): TR | ER => { return booleanFrom(isAnyFunction(pred) ? pred(x) : pred) ? thenFn(x) : elseFn(x) } /** * @description Invoke a function when the predicate result is truthy. * * @example * ``` * // Expect: 4 * const example1 = when(true, (value?: number) => (value ?? 0) + 1, 3) * // Expect: 3 * const example2 = when(false, (value?: number) => (value ?? 0) + 1, 3) * ``` */ export function when(pred: unknown, whenFn: (x?: X) => WR): WR export function when(pred: unknown, whenFn: (x?: X) => WR, x: X): WR | X export function when( pred: unknown, whenFn: (x?: X) => WR, x?: X, ): WR | X | undefined { return booleanFrom(isAnyFunction(pred) ? pred(x) : pred) ? whenFn(x) : x } /** * @description Invoke a function when the predicate result is falsy. * * @example * ``` * // Expect: 2 * const example1 = unless(false, (value?: number) => (value ?? 0) + 1, 1) * // Expect: 1 * const example2 = unless(true, (value?: number) => (value ?? 0) + 1, 1) * ``` */ export function unless(pred: unknown, unlessFn: (x?: X) => UR): UR export function unless(pred: unknown, unlessFn: (x?: X) => UR, x: X): UR export function unless( pred: unknown, unlessFn: (x?: X) => UR, x?: X, ): UR | X | undefined { return !booleanFrom(isAnyFunction(pred) ? pred(x) : pred) ? unlessFn(x) : x } /** * @description Evaluate an immediate if expression. * * @example * ``` * // Expect: "yes" * const example1 = iif(true, "yes", "no") * // Expect: "no" * const example2 = iif(false, "yes", "no") * ``` * * @see {@link booleanFrom} */ export const iif = ( cond: unknown, trueValue: X, falseValue: Y, ): X | Y => { return booleanFrom(isAnyFunction(cond) ? cond(trueValue) : cond) ? trueValue : falseValue } export type GuardCondition = [judgement: boolean, value: V] /** * @description Pick the first matching value from guard conditions. * * @example * ``` * // Expect: "b" * const example1 = guards([ * [false, "a"], * [true, "b"], * ], "z") * // Expect: "z" * const example2 = guards([[false, "a"]], "z") * ``` * * @see {@link https://hackage.haskell.org/package/Boolean-0.2.4/candidate/docs/Data-Boolean.html} * @see {@link https://hackage.haskell.org/package/Boolean-0.2.4/candidate/docs/src/Data-Boolean.html#guardedB} */ export const guards = (conditions: Array>, defaultValue: V): V => { for (const [judgement, value] of conditions) { if (judgement) { return value } } return defaultValue } export type CaseCondition = [predicate: (predicated?: P) => boolean, value: V] /** * @description Pick the first matching value from predicate cases. * * @example * ``` * // Expect: "mid" * const example1 = cases(3, [ * [(value?: number) => (value ?? 0) > 5, "big"], * [(value?: number) => (value ?? 0) > 1, "mid"], * ], "small") * // Expect: "small" * const example2 = cases(0, [[(value?: number) => (value ?? 0) > 1, "mid"]], "small") * ``` * * @see {@link https://hackage.haskell.org/package/Boolean-0.2.4/candidate/docs/src/Data-Boolean.html#caseB} */ export const cases =

( predicated: P, conditions: Array>, defaultValue: V, ): V => { for (const [predicate, value] of conditions) { if (predicate(predicated)) { return value } } return defaultValue } export interface TryCatch { (tryer: () => Target, catcher: (exception: unknown) => Target): Target (tryer: () => Target, catchallValue: Target): Target /** * @description 未提供 `catcher` 或 `catchallValue` 的情况下,若 `tryer` 抛出异常,则返回 `undefined` */ (tryer: () => Target): Target | undefined (tryer: () => void): void } /** * @description Execute a function and catch any exception, returning either a catch value or undefined. * * @example * ``` * // Expect: "ok" * const example1 = tryCatch(() => "ok", () => "error") * // Expect: "error" * const example2 = tryCatch(() => { throw new Error("fail") }, () => "error") * // Expect: 0 * const example3 = tryCatch(() => { throw new Error("fail") }, 0) * // Expect: undefined * const example4 = tryCatch(() => { throw new Error("fail") }) */ export const tryCatch: TryCatch = ( tryer: () => Target, catcherOrCatchallValue?: Target | ((exception: unknown) => Target), ): Target | undefined => { try { return tryer() } catch (exception) { // NOTE: intended redundant type guard if (catcherOrCatchallValue !== undefined) { if (isFunction(catcherOrCatchallValue)) { return catcherOrCatchallValue(exception) } else { return catcherOrCatchallValue } } else { return undefined } } }