mirror of
https://github.com/dzeiocom/libs.git
synced 2025-08-06 19:51:58 +00:00
fix: objectClone not correctly cloning a date
Signed-off-by: Avior <f.bouillon@aptatio.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
/// <reference types="jest" />
|
/// <reference types="jest" />
|
||||||
|
|
||||||
|
import { objectPick } from '../dist/ObjectUtil'
|
||||||
import { isObject, objectClean, objectClone, objectEqual, objectFind, objectGet, objectKeys, objectLoop, objectMap, objectOmit, objectRemap, objectSet, objectSize, objectSort, objectValues } from '../src/ObjectUtil'
|
import { isObject, objectClean, objectClone, objectEqual, objectFind, objectGet, objectKeys, objectLoop, objectMap, objectOmit, objectRemap, objectSet, objectSize, objectSort, objectValues } from '../src/ObjectUtil'
|
||||||
|
|
||||||
describe('Throw if parameter is not an object', () => {
|
describe('Throw if parameter is not an object', () => {
|
||||||
@@ -220,6 +221,25 @@ describe('Object Clone Tests', () => {
|
|||||||
expect(clone).not.toEqual(obj)
|
expect(clone).not.toEqual(obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('it should clone dates', () => {
|
||||||
|
const date = new Date('2025-01-02')
|
||||||
|
const obj = {
|
||||||
|
pouet: date,
|
||||||
|
}
|
||||||
|
|
||||||
|
// check base clone
|
||||||
|
const clone = objectClone(obj)
|
||||||
|
expect(clone).toEqual(obj)
|
||||||
|
|
||||||
|
// check that it is deep (date changes doesn't affect the clone)
|
||||||
|
date.setFullYear(2020)
|
||||||
|
expect(clone).not.toEqual(obj)
|
||||||
|
|
||||||
|
// check a value change doesn't affect the clone
|
||||||
|
clone.pouet = new Date()
|
||||||
|
expect(clone).not.toEqual(obj)
|
||||||
|
})
|
||||||
|
|
||||||
it('should deeply clone the object when option is set', () => {
|
it('should deeply clone the object when option is set', () => {
|
||||||
const obj = {
|
const obj = {
|
||||||
pouet: {is: 'first'},
|
pouet: {is: 'first'},
|
||||||
@@ -416,6 +436,28 @@ describe('Object Omit Tests', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Object Pick Tests', () => {
|
||||||
|
it('should pick certain elements', () => {
|
||||||
|
const obj = {a: 'a', b: 'c', c: 'b'}
|
||||||
|
expect(objectPick(obj, 'b')).toEqual({b: 'c'})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not care when key to pick is not present', () => {
|
||||||
|
const obj = {a: 'a', b: 'c', c: 'b'}
|
||||||
|
expect(objectPick(obj, 'b', 'd')).toEqual({b: 'c'})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with Object.freeze', () => {
|
||||||
|
const obj = {a: 'a', b: 'c', c: 'b'}
|
||||||
|
expect(objectPick(Object.freeze(obj), 'b', 'd')).toEqual({b: 'c'})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with an array', () => {
|
||||||
|
const obj = [1, 2, 3, 4]
|
||||||
|
expect(objectPick(obj, 1, 3)).toEqual([undefined,2,undefined,4])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('Is Object Tests', () => {
|
describe('Is Object Tests', () => {
|
||||||
it('null is not an "object"', () => {
|
it('null is not an "object"', () => {
|
||||||
expect(isObject(null)).toBe(false)
|
expect(isObject(null)).toBe(false)
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
export type BasicObjectKeys = string | number | symbol
|
export type BasicObjectKeys = string | number | symbol
|
||||||
export type BasicObject<K extends BasicObjectKeys = BasicObjectKeys, V = any> = { [P in K]?: V }
|
export type BasicObject<K extends BasicObjectKeys = BasicObjectKeys, V = any> = { [P in K]?: V }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick a from a text union only elements in Keys
|
||||||
|
*/
|
||||||
|
type TextPick<T extends BasicObjectKeys, Keys extends BasicObjectKeys> = T extends Keys ? T : never
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remap an object to an array through a function
|
* Remap an object to an array through a function
|
||||||
*
|
*
|
||||||
@@ -187,10 +192,24 @@ export function objectClone<T extends BasicObject>(obj: T, options?: { deep?: bo
|
|||||||
}
|
}
|
||||||
const clone: Partial<T> = {}
|
const clone: Partial<T> = {}
|
||||||
objectLoop(obj, (value, key) => {
|
objectLoop(obj, (value, key) => {
|
||||||
if (typeof value === 'object' && value != null && (typeof options?.deep === 'undefined' || options.deep)) {
|
if (
|
||||||
|
typeof value === 'object' // make sure child is an object
|
||||||
|
&& value != null // object is not null
|
||||||
|
&& (
|
||||||
|
typeof options?.deep === 'undefined'
|
||||||
|
|| options.deep
|
||||||
|
) // deep is set to true or undefined
|
||||||
|
&& !(value instanceof Date) // object is not a date
|
||||||
|
) {
|
||||||
clone[key as Extract<keyof T, string>] = objectClone(value)
|
clone[key as Extract<keyof T, string>] = objectClone(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// special case for Date
|
||||||
|
if (value instanceof Date) {
|
||||||
|
// @ts-expect-error value type is a Date
|
||||||
|
clone[key as Extract<keyof T, Date>] = new Date(value.getTime())
|
||||||
|
return
|
||||||
|
}
|
||||||
clone[key as Extract<keyof T, string>] = value
|
clone[key as Extract<keyof T, string>] = value
|
||||||
})
|
})
|
||||||
return clone as T
|
return clone as T
|
||||||
@@ -302,7 +321,7 @@ export function objectClean(obj: BasicObject, options?: { cleanUndefined?: boole
|
|||||||
* @param keys the keys to emit
|
* @param keys the keys to emit
|
||||||
* @returns the cloned object
|
* @returns the cloned object
|
||||||
*/
|
*/
|
||||||
export function objectOmit<T extends BasicObject>(obj: T, ...keys: Array<string | number>): T {
|
export function objectOmit<T extends BasicObject, Keys extends (keyof T | (string & {}))>(obj: T, ...keys: Array<Keys | keyof T>): Omit<T, Keys> {
|
||||||
const cloned = objectClone(obj, { deep: false })
|
const cloned = objectClone(obj, { deep: false })
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (key in cloned) {
|
if (key in cloned) {
|
||||||
@@ -392,9 +411,9 @@ export function objectGet<T = any>(obj: any, path: Array<string | number | symbo
|
|||||||
* @param keys the keys to keep
|
* @param keys the keys to keep
|
||||||
* @returns a new copy of `obj` with only `keys` in it
|
* @returns a new copy of `obj` with only `keys` in it
|
||||||
*/
|
*/
|
||||||
export function objectPick<V, K extends string | number | symbol>(obj: Record<K, V>, ...keys: Array<K>): Pick<Record<K, V>, K> {
|
export function objectPick<T extends BasicObject, Keys extends (keyof T | (string & {}))>(obj: T, ...keys: Array<Keys | keyof T>): Pick<T, TextPick<Keys, keyof T>> {
|
||||||
mustBeObject(obj)
|
mustBeObject(obj)
|
||||||
return objectFilter(obj, (_, k) => keys.includes(k)) as Pick<Record<K, V>, K>
|
return objectFilter(obj, (_, k) => keys.includes(k)) as Pick<T, Keys>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user