UNPKG

@hgargg-0710/one

Version:

A tiny npm library purposed for providing beautiful solutions to frequent miniature (one-line/one-expression) tasks for various JS datatypes.

265 lines (264 loc) 8.93 kB
import { equals, T } from "../boolean/boolean.js"; import { isArray, isStruct } from "../type/type.js"; import { same as array_same } from "../array/array.js"; /** * Returns the pair of keys and values of the given object */ export const kv = (obj) => [keys(obj), values(obj)]; /** * Creates a new object using the pair of keys and values */ export const dekv = ([keys, values]) => { const result = empty(); for (let i = 0; i < keys.length; ++i) result[keys[i]] = values[i]; return result; }; /** * Creates a predicate for type-checking object-shapes. * * For `true`, mandates that argument be of `typeof x === "object"`. For `null`, returns `false` * * @param properties The basic object shape. If an array - items from the array are treated as keys to be checked for presence on the passed `x`. Otherwise - an object, keys of which correspond to property names to be proven to be present, and to be passing of the predicates assigned to the respective keys. Defaults to `[]` * @param lacks The array of keys to be shown to definitively not be present on the object. Checked before `optional`. Defaults to `[]` * @param optional The array of properties to be allowed on the object in the event that `isStrict` is set to `true`. Defaults to `[]` * @param isStrict The flag for indicating whether the shape to be checked for is "strict", meaning - whether any properties other than `properties` and `optional` are allowed. Defaults to `false` * * @example * * interface IThing { * eatable: any * fruit: bool * } * * interface Fruit extends IThing { * eatable: true * fruit: true * } * * interface Shelf extends IThing { * fruit: false * eatable: "maybe???" * robust: true * } * * const makeThing = (eatable: any, fruit: bool): IThing => ({ eatable, fruit }) * const makeFruit = (): Fruit => makeThing(true, true) * const makeShelf = (): Shelf => { ...makeThing("maybe???", false), robust: true } * * const isThing = structCheck<IThing>({ * eatable: T, * fruit: isBoolean, * }) * * const isThingExact = structCheck<IThing>({ * eatable: T, * fruit: isBoolean, * }, [], [], true) * * const isFruit = structCheck<Fruit>({ * eatable: eqcurry(true), * fruit: eqcurry(true), * }) * * const isShelf = structCheck<Shelf>({ * eatable: eqcurry("maybe???"), * fruit: eqcurry(false) * }) * * const apple = makeFruit() * const shelf = makeShelf() * * isThing(shelf) // true * isFruit(shelf) // false * isShelf(shelf) // true * * isThing(fruit) // true * isFruit(fruit) // true * isShelf(fruit) // false */ export function structCheck(properties, lacks = [], optional = [], isStrict = false) { const props = isArray(properties) ? Array.from(new Set(properties)) : keys(properties); const propsPredicateArrays = isArray(properties) ? [] : values(properties); return (x) => { if (!(isStruct(x) && props.every((p) => p in x) && lacks.every((p) => !(p in x)) && propsPredicateArrays.every((pred, i) => (pred || T)(x[props[i]])))) return false; if (!isStrict) return true; const currKeys = keys(x); const keyslen = keys(x).length; const proplen = props.length; if (proplen === keyslen) return true; if (keyslen - proplen > optional.length) return false; const difference = Array.from(new Set(currKeys).difference(new Set(props))); return difference.every((key) => optional.includes(key)); }; } /** * Returns the list of keys of a given object [includes the prototypes] */ export function keys(object) { const props = recursiveStringKeys(object); props.push(...recursiveSymbolKeys(object)); return props; } /** * Returns the array of object values [includes the prototypes] */ export function values(object) { const vals = recursiveStringValues(object); vals.push(...recursiveSymbolValues(object)); return vals; } /** * Returns the array of string keys of a given object [includes the prototypes] */ export function recursiveStringKeys(object) { const props = []; for (const p in object) props.push(p); return props; } /** * Returns the array of values of a given object at string keys [includes the prototypes] */ export function recursiveStringValues(object) { const props = []; for (const p in object) props.push(object[p]); return props; } /** * Returns the array of symbol keys of a given object [includes the prototypes] */ export function recursiveSymbolKeys(object) { const symbolProperties = Object.getOwnPropertySymbols(object); let proto; while ((proto = prototype(object))) symbolProperties.push(...Object.getOwnPropertySymbols((object = proto))); return symbolProperties; } /** * Returns the array of values of a given object at symbol keys [includes the prototypes] */ export const recursiveSymbolValues = (object) => recursiveSymbolKeys(object).map((key) => object[key]); /** * Returns the pair of own keys and own properties of a given object */ export const ownProperties = (object) => [ ownKeys(object), ownValues(object) ]; /** * Returns the own keys of a given object */ export function ownKeys(object) { const keys = Object.getOwnPropertyNames(object); keys.push(...Object.getOwnPropertySymbols(object)); return keys; } /** * Returns the own values of a given object */ export function ownValues(object) { return ownKeys(object).map((key) => object[key]); } /** * Alias of 'Object.getPrototypeOf' */ export const prototype = Object.getPrototypeOf; /** * Makes a shallow copy of a given object */ export const copy = (x) => ({ ...x }); /** * Returns the object containing all the property descriptors on a given object `object` [includes the prototypes, respects inheritance], * up to the point of meeting the object of `commonPrototype` in the prototype chain (defaults to `Object.prototype`). */ export function propertyDescriptors(object, commonPrototype = Object.prototype) { let currPrototype = object; let final = Object.getOwnPropertyDescriptors(object); while (prototype(currPrototype) !== commonPrototype) final = { ...final, ...findOwnMissing(final, Object.getOwnPropertyDescriptors((currPrototype = prototype(currPrototype)))) }; return final; } /** * Returns a new object containing all the own properties of `atobj` not present in `inobj` */ export function findOwnMissing(inobj, atobj) { const final = empty(); for (const x of ownKeys(atobj)) if (!(x in inobj)) final[x] = atobj[x]; return final; } /** * Allocates a new empty array */ export const empty = () => ({}); /** * Creates a function that returns the shallow copy of `object` [useful for preserving the object's] */ export const allocator = (object) => () => copy(object); /** * Returns whether the two given objects are the same up to direct equivalence of keys' names, * and equivalence of values' arrays via 'array.same' with `pred` predicate */ export const same = (x, y, pred) => array_same(keys(x), keys(y)) && array_same(values(x), values(y), pred); /** * Recursively compares the two objects `x` and `y`, applying * `pred(x[i], y[i], i)` on all the values of `x` and `y` that aren't objects themselves. * * `pred` defaults to `equals` */ export function recursiveSame(x, y, pred = equals) { const yvals = values(y); return (array_same(keys(x), keys(y)) && values(x).every((x, i) => isStruct(x) && isStruct(yvals[i]) ? recursiveSame(x, yvals[i], pred) : pred(x, yvals[i], i))); } /** * Returns a copy of a given object without the provided properties `props` */ export function withoutProperties(...props) { const propsSet = new Set(props); return function (object) { const newObj = empty(); for (const prop of keys(object)) if (!propsSet.has(prop)) newObj[prop] = object[prop]; return newObj; }; } /** * Returns a function for obtaining `x[name]` */ export const prop = (name) => (x) => x[name]; /** * Alias of 'Object.defineProperty' */ export const propDefine = Object.defineProperty; /** * Alias of 'Object.defineProperties' */ export const propsDefine = Object.defineProperties; /** * Defines a property with a name `name` and value described by the property-descriptor `value` * on the `Extended.prototype` */ export const protoProp = (Extended, name, value) => propDefine(Extended.prototype, name, value); /** * Defines the properties described by the given property-descriptor-map `properties` on `Extended.prototype` */ export const extendPrototype = (Extended, properties) => propsDefine(Extended.prototype, properties); export * as classes from "./classes.js"; export * as descriptor from "./descriptor.js";