/** * Functions that does nothing (useful for default callbacks) */ export function nop(...args: any[]): any { } /** * Identity function (returns the sole argument untouched) */ export function identity(input: T): T { return input; } /** * Partially apply fixed arguments to a function with named arguments */ export function partial, RA extends any[], R>(fixedargs: PA, func: (args: A, ...rest: RA) => R): (args: Omit, ...rest: RA) => R { return (args: Omit, ...rest: RA) => func({ ...fixedargs, ...args } as any, ...rest); } type cmpArgs = Readonly<{ key: (item: any) => any, reverse: boolean }> const cmpDefaults: cmpArgs = { key: identity, reverse: false } /** * Compare operator, that can be used in sort() calls. */ export function cmp(args?: Partial): (a: any, b: any) => number { const fargs = { ...cmpDefaults, ...args }; return (a, b) => { const ka = fargs.key(a); const kb = fargs.key(b); if (ka > kb) { return fargs.reverse ? -1 : 1; } else if (ka < kb) { return fargs.reverse ? 1 : -1; } else { return 0; } } } /** * Predicate that always returns true */ export const always = (...args: any[]) => true /** * Predicate that always returns false */ export const never = (...args: any[]) => false /** * Apply a boolean "and" to merge predicates */ export function and(...predicates: ((...args: A) => boolean)[]): (...args: A) => boolean { return (...args) => { for (let p of predicates) { if (!p(...args)) { return false; } } return true; } } /** * Apply a boolean "or" to merge predicates */ export function or(...predicates: ((...args: A) => boolean)[]): (...args: A) => boolean { return (...args) => { for (let p of predicates) { if (p(...args)) { return true; } } return false; } } /** * Pipe the result of a function as first parameter of another, to create a new function */ export function pipe(f1: (...args: IN) => INT, f2: (value: INT) => R): (...args: IN) => R { return (...args: IN) => f2(f1(...args)); } /** * Attribute getter */ export function attr(name: K): >(obj: O) => V { return obj => obj[name]; } /** * Check that a value is not null, throwing an error if it is */ export function nn(value: T | null): T { if (value === null) { throw new Error("null value"); } else { return value; } } /** * Check that a value is not undefined, throwing an error if it is */ export function nu(value: T | undefined): T { if (typeof value === "undefined") { throw new Error("undefined value"); } else { return value; } } /** * Check that a value is not null nor undefined, throwing an error if it is */ export const nnu: (value: T | null | undefined) => T = pipe(nn, nu); /** * Convert a value to a boolean (are considered falsy: 0, false, "", {}, [], null, undefined) */ export function bool(value: T | null | undefined): value is T; export function bool(value: any): boolean { if (!value) { return false; } else if (value instanceof Set) { return value.size > 0; } else if (typeof value == "object") { return Object.keys(value).length > 0; } else { return true; } } /** * Applies map and filter on an array, using a single function */ export function fmap(m?: undefined, f?: (val: A) => val is T): (array: A[]) => T[]; export function fmap(m?: undefined, f?: (val: A) => boolean): (array: A[]) => A[]; export function fmap(m: (val: A) => B, f?: (val: B) => val is T): (array: A[]) => T[]; export function fmap(m: (val: A) => B, f?: (val: B) => boolean): (array: A[]) => B[]; export function fmap(m?: (val: A) => B, f?: (val: A | B) => boolean): (array: A[]) => (A | B)[] { return array => { if (m && f) { return array.map(m).filter(f); } else if (m) { return array.map(m); } else if (f) { return array.filter(f); } else { return array.slice(); } }; }