npm i merge-anything
Fusiona objetos y otros tipos de forma recursiva. ¡Totalmente compatible con TypeScript ! Una integración simple y pequeña.
Creé este paquete porque probé muchos paquetes similares que fusionan/mergen profundamente/asignan objetos recursivos, etc. Pero todos tenían sus peculiaridades, y todos rompen cosas que se supone que no deben romper ...
Estaba buscando:
Object.assign()
pero profunda ¡Este último es crucial! En JavaScript casi todo es un objeto , claro, pero no quiero que una función de combinación intente fusionar, por ejemplo. ¡Dos new Date()
! Muchas bibliotecas utilizan clases personalizadas que crean objetos con prototipos especiales, y todos esos objetos se rompen al intentar fusionarlos. ¡Así que debemos tener cuidado!
merge-anything fusionará objetos y propiedades anidadas, pero sólo mientras sean "objetos simples". Tan pronto como un sub-prop no sea un "objeto simple" y tenga un prototipo especial, copiará esa instancia "tal cual". ♻️
import { merge } from 'merge-anything'
const starter = { name : 'Squirtle' , types : { water : true } }
const newValues = { name : 'Wartortle' , types : { fighting : true } , level : 16 }
const evolution = merge ( starter , newValues , { is : 'cool' } )
// returns {
// name: 'Wartortle',
// types: { water: true, fighting: true },
// level: 16,
// is: 'cool'
// }
En el ejemplo anterior, si está utilizando TypeScript y pasa el cursor sobre evolution
, podrá ver el tipo de su nuevo objeto en ese mismo momento. Esto es muy poderoso, porque puedes fusionar cosas y, sin necesidad any
, TypeScript sabrá exactamente cómo se ven los objetos recién fusionados.
El tipo de retorno de la función merge()
también se puede utilizar como una utilidad TypeScript:
import type { Merge } from 'merge-anything'
type A1 = { name : string }
type A2 = { types : { water : boolean } }
type A3 = { types : { fighting : boolean } }
type Result = Merge < A1 , [ A2 , A3 ] >
Este paquete recorrerá recursivamente objetos simples y fusionará los valores en un nuevo objeto.
Tenga en cuenta que este paquete reconoce objetos JavaScript especiales como instancias de clase. En tales casos, no los fusionará recursivamente como objetos, sino que asignará la clase al nuevo objeto "tal cual".
// all passed objects do not get modified
const a = { a : 'a' }
const b = { b : 'b' }
const c = { c : 'c' }
const result = merge ( a , b , c )
// a === {a: 'a'}
// b === {b: 'b'}
// c === {c: 'c'}
// result === {a: 'a', b: 'b', c: 'c'}
// However, be careful with JavaScript object references with nested props. See below: A note on JavaScript object references
// arrays get overwritten
// (for "concat" logic, see Extensions below)
merge ( { array : [ 'a' ] } , { array : [ 'b' ] } ) // returns {array: ['b']}
// empty objects merge into objects
merge ( { obj : { prop : 'a' } } , { obj : { } } ) // returns {obj: {prop: 'a'}}
// but non-objects overwrite objects
merge ( { obj : { prop : 'a' } } , { obj : null } ) // returns {obj: null}
// and empty objects overwrite non-objects
merge ( { prop : 'a' } , { prop : { } } ) // returns {prop: {}}
fusionar cualquier cosa mantiene correctamente intactos los objetos especiales como fechas, expresiones regulares, funciones, instancias de clases, etc.
Sin embargo, es muy importante que comprenda cómo solucionar las referencias a objetos de JavaScript. Asegúrese de leer #a nota sobre las referencias de objetos de JavaScript a continuación.
El comportamiento predeterminado es que las matrices se sobrescriben. Puede importar mergeAndConcat
si necesita concatenar matrices. Pero no se preocupe si no lo necesita, ¡esta biblioteca se puede modificar en árbol y no importará código que no utilice!
import { mergeAndConcat } from 'merge-anything'
mergeAndConcat (
{ nested : { prop : { array : [ 'a' ] } } } ,
{ nested : { prop : { array : [ 'b' ] } } }
)
// returns { nested: { prop: { array: ['a', 'b'] } } },
Puede haber ocasiones en las que necesites modificar la lógica cuando se fusionan dos cosas. Puede proporcionar su propia función personalizada que se activa cada vez que se sobrescribe un valor.
Para este caso usamos mergeAndCompare
. A continuación se muestra un ejemplo con una función de comparación que concatena cadenas:
import { mergeAndCompare } from 'merge-anything'
function concatStrings ( originVal , newVal , key ) {
if ( typeof originVal === 'string' && typeof newVal === 'string' ) {
// concat logic
return ` ${ originVal } ${ newVal } `
}
// always return newVal as fallback!!
return newVal
}
mergeAndCompare ( concatStrings , { name : 'John' } , { name : 'Simth' } )
// returns { name: 'JohnSmith' }
Nota para usuarios de TypeScript. Es posible que el tipo devuelto por esta función no sea correcto. En ese caso, debe transmitir el resultado a su propia interfaz proporcionada.
Tenga cuidado con las referencias a objetos de JavaScript. ¡Cualquier propiedad anidada será reactiva y vinculada entre los objetos originales y fusionados! A continuación le mostraremos cómo prevenir esto.
const original = { airport : { status : 'dep. ?' } }
const extraInfo = { airport : { location : 'Brussels' } }
const merged = merge ( original , extraInfo )
// we change the status from departuring ? to landing ?
merged . airport . status = 'lan. ?'
// the `merged` value will be modified
// merged.airport.status === 'lan. ?'
// However `original` value will also be modified!!
// original.airport.status === 'lan. ?'
La regla clave a recordar es:
Cualquier propiedad que esté anidada en más de 1 nivel sin una propiedad principal superpuesta será reactiva y vinculada tanto en el resultado de la combinación como en la fuente.
Sin embargo, existe una solución realmente sencilla . Podemos simplemente copiar el resultado de la fusión para eliminar cualquier reactividad. Para ello podemos utilizar la biblioteca copiar cualquier cosa. ¡Esta biblioteca también garantiza que las instancias de clases especiales no se rompan , por lo que puedes usarla sin temor a romper cosas!
Vea a continuación cómo integramos 'copiar cualquier cosa':
import { copy } from 'copy-anything'
const original = { airport : { status : 'dep. ?' } }
const extraInfo = { airport : { location : 'Brussels' } }
const merged = copy ( merge ( original , extraInfo ) )
// we change the status from departuring ? to landing ?
merged . airport . status = 'lan. ?' ( merged . airport . status === 'lan. ?' ) (
// true
// `original` won't be modified!
original . airport . status === 'dep. ?'
) // true
Luego puede probar dónde desea colocar la función copy()
.
Copy Anything también es totalmente compatible con TypeScript.
Literalmente, se trata simplemente de revisar un objeto de forma recursiva y asignar los valores a un nuevo objeto como se muestra a continuación. Sin embargo, está ajustado para permitir parámetros adicionales, etc. El siguiente código es la integración básica, que le permitirá comprender los conceptos básicos de cómo funciona.
import { isPlainObject } from 'is-what'
function mergeRecursively ( origin , newComer ) {
if ( ! isPlainObject ( newComer ) ) return newComer
// define newObject to merge all values upon
const newObject = isPlainObject ( origin )
? Object . keys ( origin ) . reduce ( ( carry , key ) => {
const targetVal = origin [ key ]
if ( ! Object . keys ( newComer ) . includes ( key ) ) carry [ key ] = targetVal
return carry
} , { } )
: { }
return Object . keys ( newComer ) . reduce ( ( carry , key ) => {
const newVal = newComer [ key ]
const targetVal = origin [ key ]
// early return when targetVal === undefined
if ( targetVal === undefined ) {
carry [ key ] = newVal
return carry
}
// When newVal is an object do the merge recursively
if ( isPlainObject ( newVal ) ) {
carry [ key ] = mergeRecursively ( targetVal , newVal )
return carry
}
// all the rest
carry [ key ] = newVal
return carry
} , newObject )
}
* Por supuesto, existen pequeñas diferencias con el código fuente real para hacer frente a casos raros y funciones adicionales. El código fuente real está aquí.