diff --git a/packages/fjl/src/object/assignDeep.ts b/packages/fjl/src/object/assignDeep.ts index b8a5cfcf..0e5c2074 100644 --- a/packages/fjl/src/object/assignDeep.ts +++ b/packages/fjl/src/object/assignDeep.ts @@ -17,7 +17,7 @@ export const // If property being visited is not settable, skip it if (propDescriptor && ( propDescriptor.writable === false || ( - propDescriptor.writable === undefined && + !propDescriptor.writable && !propDescriptor.set ) )) { @@ -28,13 +28,10 @@ export const value = obj[key]; // If LHV is not a primitive, and RHV contains enumerables then values are assignable - if (!isPrimitive(existingValue) && - containsEnumerables(value) - ) { + if (!isPrimitive(existingValue) && containsEnumerables(value)) assignDeep(existingValue, value); - } else { + else agg[key] = value; - } return agg; }, topAgg); diff --git a/packages/fjl/src/object/assocList.ts b/packages/fjl/src/object/assocList.ts index 318408f1..3bfd6c1c 100644 --- a/packages/fjl/src/object/assocList.ts +++ b/packages/fjl/src/object/assocList.ts @@ -17,7 +17,7 @@ export const /** * Returns an associated list from given object. */ - toAssocList = (obj: T): [keyof T, any][] => Object.entries(obj) as [keyof T, any][], + toAssocList = Object.entries, /** * @deprecated Not to many algorithms require this kind of @@ -39,10 +39,8 @@ export const /** * From associated list to zero object. */ - fromAssocList = (xs, zero = {}) => foldl((agg, [key, value]) => { - agg[key] = value; - return agg; - }, zero, xs), + fromAssocList = Object.fromEntries ?? ((xs, zero = {}) => foldl((agg, [key, value]) => ( + agg[key] = value, agg), zero, xs)), /** * @deprecated Method is too specific - Define functionality/method diff --git a/packages/fjl/src/object/is.ts b/packages/fjl/src/object/is.ts index a8931c15..c280772a 100644 --- a/packages/fjl/src/object/is.ts +++ b/packages/fjl/src/object/is.ts @@ -3,7 +3,7 @@ */ import {typeOf} from './typeOf'; -import {instanceOf, keys} from '../platform/object'; +import {hasOwnProperty, instanceOf, keys} from '../platform/object'; import {isset} from './isset'; import {Constructable, TypeRef, Unary} from "../types"; @@ -70,8 +70,8 @@ export const isFunction = (x: any): boolean => instanceOf(Function, x), /** - * @deprecated Use `instanceOf` instead (checks both value's - * constructor, against passed in type, else `instanceof` check). + * @deprecated Use `instanceOf` instead (performs equality check on constructors + * and, ultimately, performs `instanceof` check, if constructors are not the same). * * Strict type checker. Checks if given value is a direct instance of given type; E.g., * @example @@ -88,21 +88,6 @@ export const */ isType = (type: TypeRef | any, obj: any): boolean => typeOf(obj) === toTypeRefName(type), - /** - * @deprecated Use `$instanceOf` instead (checks both value's - * constructor, against passed in type, else `instanceof` check). - */ - $isType = (type: TypeRef | any) => - (obj: any): boolean => isType(type, obj), - - /** - * @deprecated Use `instanceOf` instead (checks both value's - * constructor, against passed in type, else `instanceof` check). - * - * Synonym for `isType` (or just a more accurate name for `isType`). - */ - isStrictly = isType, - /** * Checks if given value contains a constructor equal to one of the passed in ones, * or if it is an instance of one of the passed in ones. @@ -124,6 +109,8 @@ export const * isInstanceOf([], Array) === true * isInstanceOf({}, Object) === true * ``` + * + * @todo consolidate implementation into `instanceOf`. */ isInstanceOf = (x: any, ...types: Constructable[]): boolean => types.some(t => instanceOf(t, x)), @@ -153,20 +140,13 @@ export const */ isOfType = (type, x) => isType(type, x) || instanceOf(type, x), - /** - * @deprecated Use `isInstanceOf`. - * - * Synonym for `isInstanceOf` (or just a more accurate name). - */ - isLoosely = isInstanceOf, - /** * Checks if `value` is an es2015 (user defined) `class`. */ isClass = (x: any): boolean => x && _classPrefixRegex.test((x + '').substring(0, 11)), /** - * Returns a boolean depicting whether a value is callable or not. + * Returns a boolean denoting whether a value is callable or not. */ isCallable = (x: any): boolean => isFunction(x) && !isClass(x), @@ -182,11 +162,12 @@ export const /** * Checks if value is a boolean. + * @todo Replace instances of `isset(...)` with optional chaining (since it is now in the spec). */ isBoolean = x => isset(x) && x.constructor === Boolean, /** - * Checks if value is a number, and/or a bigint, and not `NaN`. + * Checks if value is not `NaN`, and is a number, and/or a bigint. * * ```typescript * isNumber(99) === true; @@ -216,34 +197,6 @@ export const isString = x => isset(x) && x.constructor === String, - /** - * @deprecated Perform `instanceof` check instead. - * - * Checks whether value is of `Map` or not. - */ - isMap = x => isset(x) && x instanceof Map, - - /** - * @deprecated Perform `instanceof` check instead. - * - * Checks whether value is of `Set` or not. - */ - isSet = $isType(Set.name) as Unary, - - /** - * @deprecated Perform `instanceof` check instead. - * - * Checks whether value is of `WeakMap` or not. - */ - isWeakMap = $isType(WeakMap.name) as Unary, - - /** - * @deprecated Perform `instanceof` check instead. - * - * Checks whether value is of `WeakSet` or not. - */ - isWeakSet = $isType(WeakSet.name) as Unary, - /** * Checks if value is undefined. */ @@ -272,48 +225,22 @@ export const }, /** - * Checks if given value is not `null`, not `undefined`, and is a constructable primitive (e.g., instance/literal of - * one of `String`, `Boolean`, `Number`, `BigInt`, and/or, `Symbol`); + * Checks if given value is not `null`, not `undefined`, and is a constructable primitive ( + * e.g., instance/literal of one of `String`, `Boolean`, `Number`, `BigInt`, and/or, `Symbol`); */ isConstructablePrimitive = (x: any): boolean => !isset(x) ? false : _primitive_constructors .some(type => instanceOf(type, x)), - /** - * @deprecated Use `isConstructablePrimitive` instead. - */ - isUsableImmutablePrimitive = isConstructablePrimitive, - - /** - * @deprecated check length directly and/or create a new method for - * the purpose; e.g., `const notLength = x => !(x?.length)`, etc. - * - * Checks if !length. - */ - isEmptyList = (x: any): boolean => !(x?.length), - /** * Checks if object contains enumerable properties or not. - * @todo requires tests. + * @todo write tests. */ containsEnumerables = (x: any): boolean => isset(x) && keys(x).length > 0, - /** - * @deprecated Use `containsEnumerables`. - */ - isEmptyObject = containsEnumerables, /** - * @deprecated Check values directly. - * - * Checks if collection is empty or not (Map, WeakMap, WeakSet, Set etc.). - */ - isEmptyCollection = (x: { readonly size: number }): boolean => x.size === 0, - - /** - * @deprecated Check values/value type states directly. - * * Checks if passed in value is falsy, an empty array, * an empty es6 collection object (Map, Set, etc.), * and/or, contains no enumerable properties/is an empty object (of @@ -323,9 +250,7 @@ export const // if '', `0`/`0n`, `null`, `undefined`, `NaN`, or `false` then value is empty if (!x) return true; - const {constructor} = x; - - switch (constructor) { + switch (x.constructor) { // If is a constructable primitive, it's not empty (at this point) case String: case Number: @@ -336,52 +261,10 @@ export const default: if (isInstanceOf(x, Map, Set, WeakSet, WeakMap)) return !x.size; - if (x?.length) return false; + if (x.length) return false; // Else check if object doesn't contain enumerable properties return !keys(x).length; } - }, - - /** - * @deprecated - Use `isInstanceOf` method. - * - * Checks to see if `x` is of one of the given type refs; Strict - * type check (not-instanceof check). - * - * @todo write tests for this function. - */ - isOneOf = (x: any, ...types: any): boolean => { - const typeName = typeOf(x); - return toTypeRefNames(types).some(name => typeName === name); - }, - - /** - * @deprecated Use `isInstanceOf` method. - * - * Checks if given value is strictly one of given types. - */ - isStrictlyOneOf = isOneOf, - - /** - * @deprecated Use `isInstanceOf` instead. - * - * Checks if given value is either strictly one of given types or is - * an `instanceof` one of given types. - */ - isLooselyOneOf = (x: any, ...types: any): boolean => - types.some(type => isType(type, x) || instanceOf(x, type)), - - /** - * @deprecated Use `isInstanceOf`. - */ - instanceOfOne = isInstanceOf, - - /** - * @deprecated Checks types directly. - * - * Checks if value qualifies (has `map` method) as a functor. - */ - isFunctor = (x: any): boolean => isset(x) && isFunction(x.map) - + } ; diff --git a/packages/fjl/src/object/typeOf.ts b/packages/fjl/src/object/typeOf.ts index 125336ed..0fff65dc 100644 --- a/packages/fjl/src/object/typeOf.ts +++ b/packages/fjl/src/object/typeOf.ts @@ -15,18 +15,16 @@ const _NaN = 'NaN', * `undefined` and 'Null' for `null`. */ export function typeOf(value: any): string { - let retVal; if (value === undefined) { - retVal = _Undefined; + return _Undefined; } else if (value === null) { - retVal = _Null; + return _Null; } else { const {name: constructorName} = value.constructor; - retVal = ( + return ( constructorName === Number.name || constructorName === BigInt.name ) && Number.isNaN(value) ? _NaN : constructorName; } - return retVal; } diff --git a/packages/fjl/src/platform/object/index.ts b/packages/fjl/src/platform/object/index.ts index f55215f1..e60792a5 100644 --- a/packages/fjl/src/platform/object/index.ts +++ b/packages/fjl/src/platform/object/index.ts @@ -11,26 +11,6 @@ export const // @todo We shouldn' be returning these directly. {assign, keys} = Object, - /** - * @todo Method should take object as first argument. - */ - instanceOf = (X: Constructable, x): boolean => - isset(x) && x.constructor === X || x instanceof X, // @todo remove null check (isset) here (in future release). - - $instanceOf = (X: Constructable) => (x): boolean => instanceOf(X, x), - - /** - * @todo Method should take object as first argument. - */ - hasOwnProperty = (key: string | PropertyKey, x: T): boolean => - // @note `Object.hasOwn` cannot be used here until it is more broadly - // adopted (until node v24+ release etc.). - isset(x) && Object.prototype.hasOwnProperty.call(x, key) // @todo shouldn't be checking for null/undefined here - , - - $hasOwnProperty = (key: string | PropertyKey) => - (x: T): boolean => hasOwnProperty(key, x), - /** * Contains all the static functions from `Object` but curried and flipped; * Methods that only take one parameter, or only 'rest' args, are exported as is. @@ -86,6 +66,26 @@ export const break; } return agg; - }, {}) as ObjectStatics + }, {}) as ObjectStatics, + + /** + * @todo Method should take object as first argument. + */ + instanceOf = (X: Constructable, x): boolean => + isset(x) && x.constructor === X || x instanceof X, // @todo remove null check (isset) here (in future release). + + $instanceOf = (X: Constructable) => (x): boolean => instanceOf(X, x), + + /** + * @todo Method should take object as first argument. + */ + hasOwnProperty = native.hasOwn ?? ((key: string | PropertyKey, x: T): boolean => + // @note `Object.hasOwn` cannot be used here until it is more broadly + // adopted (until node v24+ release etc.). + isset(x) && Object.prototype.hasOwnProperty.call(x, key)) // @todo shouldn't be checking for null/undefined here + , + + $hasOwnProperty = (key: string | PropertyKey) => + (x: T): boolean => hasOwnProperty(key, x) ; diff --git a/packages/fjl/src/types/native.ts b/packages/fjl/src/types/native.ts index 4cc55796..18c7a004 100644 --- a/packages/fjl/src/types/native.ts +++ b/packages/fjl/src/types/native.ts @@ -8,7 +8,7 @@ export type CreateFunc = (pdm: PropertyDescriptorMap, applicand: any) => any; export type IsFunc = (Type: any, value: any) => boolean; -// @todo Need to added es > es6 methods below. +// @todo Need to add es > es6 methods below. export interface ObjectStatics { assign: (...xs: any[]) => any; create: CreateFunc; @@ -22,6 +22,7 @@ export interface ObjectStatics { getOwnPropertyNames: (x: any) => string[]; getOwnPropertySymbols: (x: any) => symbol[]; getPrototypeOf: (x: any) => object | null; + hasOwn: (propName: string | PropertyKey, x: any) => boolean; is: IsFunc; isExtensible: (x: any) => boolean; isFrozen: (x: any) => boolean; diff --git a/packages/fjl/tests/list/test-isEmpty.ts b/packages/fjl/tests/list/test-isEmpty.ts deleted file mode 100644 index ec248b17..00000000 --- a/packages/fjl/tests/list/test-isEmpty.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {expectEqual, expectError, vowelsArray, vowelsString} from "../helpers"; -import {isEmptyList} from "../../src/object/is"; - -describe('#isEmpty (a.k.a. #`null`)', () => { - it ('should return a boolean indicating whether given list is empty or not', () => { - [[vowelsString, false], [vowelsArray, false], ['', true], [[], true]] - .forEach(([subj, expected]) => { - expectEqual(isEmptyList(subj), expected); - }); - }); - it('should throw an error when receiving non list-like (doesn\'t have a `length` prop)', () => { - [{}, null, undefined, false] - .forEach(x => expectError((_x => () => isEmptyList(_x))(x))); - expectError(isEmptyList); - }); -}); diff --git a/packages/fjl/tests/object/index_test.ts b/packages/fjl/tests/object/index_test.ts index a62795cf..686594a5 100644 --- a/packages/fjl/tests/object/index_test.ts +++ b/packages/fjl/tests/object/index_test.ts @@ -22,18 +22,14 @@ import { isBoolean, isEmpty, isFunction, - isMap, isNull, isNumber, isObject, - isSet, isset, isString, isSymbol, isType, isUndefined, - isWeakMap, - isWeakSet, jsonClone, keys, log, @@ -349,88 +345,6 @@ describe('#object', function () { }); }); - if (typeof Map !== 'undefined') { - describe('#isMap', function () { - it('should return expected result for given value', function () { - // [`arg`, `expected`] - [ - ['', false], - [vowelsString, false], - [0 / 0, false], - [() => undefined, false], - [new Map(), true], - [new Map(vowelsArray.map(c => [c, c.charCodeAt(0)])), true], - [false, false], - [0, false], - [undefined, false], - [null, false], - ] - .forEach(([arg, expected]) => expect(isMap(arg)).toEqual(expected)); - }); - }); - } - - if (typeof Set !== 'undefined') { - describe('#isSet', function () { - it('should return expected result for given value', function () { - // [`arg`, `expected`] - [ - ['', false], - [vowelsString, false], - [0 / 0, false], - [() => undefined, false], - [new Set(), true], - [new Set(vowelsArray), true], - [false, false], - [0, false], - [undefined, false], - [null, false], - ] - .forEach(([arg, expected]) => expect(isSet(arg)).toEqual(expected)); - }); - }); - } - - if (typeof WeakMap !== 'undefined') { - describe('#isWeakMap', function () { - it('should return expected result for given value', function () { - // [`arg`, `expected`] - [ - ['', false], - [vowelsString, false], - [0 / 0, false], - [() => undefined, false], - [new WeakMap(), true], - [false, false], - [0, false], - [undefined, false], - [null, false], - ] - .forEach(([arg, expected]) => expect(isWeakMap(arg)).toEqual(expected)); - }); - }); - } - - if (typeof WeakSet !== 'undefined') { - describe('#isWeakSet', function () { - it('should return expected result for given value', function () { - // [`arg`, `expected`] - [ - ['', false], - [vowelsString, false], - [0 / 0, false], - [() => undefined, false], - [new WeakSet(), true], - [false, false], - [0, false], - [undefined, false], - [null, false], - ] - .forEach(([arg, expected]) => expect(isWeakSet(arg)).toEqual(expected)); - }); - }); - } - describe('#isUndefined', function () { it('should return expected result for given value', function () { // [`arg`, `expected`] @@ -488,7 +402,6 @@ describe('#object', function () { }); describe('#isEmpty', () => { - (<[Parameters[0], ReturnType][]>[ // Empties [null, true],