npm i merge-anything
객체 및 기타 유형을 재귀적으로 병합합니다. TypeScript가 완전히 지원됩니다! 간단하고 작은 통합.
병합/심층 병합/재귀 객체 할당 등을 수행하는 유사한 패키지를 많이 시도했기 때문에 이 패키지를 만들었습니다. 그러나 모두 고유한 단점이 있었고 모두 깨져서는 안되는 것들을 깨뜨렸습니다 ...?
내가 찾고 있던 것 :
Object.assign()
과 같은 간단한 병합 함수이지만 깊이가 있습니다. 이 마지막 것이 중요합니다! JavaScript에서는 거의 모든 것이 object 입니다. 하지만 병합을 시도하는 병합 함수를 원하지 않습니다. 두 개의 new Date()
인스턴스! 많은 라이브러리가 특별한 프로토타입으로 개체를 생성하는 사용자 정의 클래스를 사용하며, 이러한 개체는 병합하려고 하면 모두 중단됩니다. 그러니 우리는 조심해야 해요!
merge-anything은 개체와 중첩된 속성을 병합하지만 "일반 개체"인 경우에만 해당됩니다. 하위 소품이 "일반 개체"가 아니고 특별한 프로토타입을 가지면 해당 인스턴스를 "있는 그대로" 복사합니다. ♻️
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'
// }
위의 예에서 TypeScript를 사용하고 있고 evolution
위로 마우스를 가져가면 실제로 새 개체의 유형을 바로 그 자리에서 볼 수 있습니다. 이는 매우 강력합니다. 항목을 병합할 수 있고 any
필요하지 않고 TypeScript가 새로 병합된 개체가 어떻게 보이는지 정확히 알 수 있기 때문입니다!
merge()
함수의 반환 유형은 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 ] >
이 패키지는 일반 객체를 재귀적으로 처리하고 값을 새 객체에 병합합니다.
이 패키지는 클래스 인스턴스와 같은 특수 JavaScript 개체를 인식합니다. 이러한 경우 객체처럼 재귀적으로 병합하지 않고 클래스를 "있는 그대로" 새 객체에 할당합니다!
// 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: {}}
아무것도 병합하면 날짜, 정규 표현식, 함수, 클래스 인스턴스 등과 같은 특수 객체를 그대로 유지합니다.
그러나 JavaScript 개체 참조를 해결하는 방법을 이해하는 것이 매우 중요합니다 . 아래의 #a JavaScript 개체 참조에 대한 참고 사항을 읽어보세요.
기본 동작은 배열을 덮어쓰는 것입니다. 배열을 연결해야 하는 경우 mergeAndConcat
가져올 수 있습니다. 하지만 이것이 필요하지 않더라도 걱정하지 마세요. 이 라이브러리는 트리 쉐이킹이 가능하며 사용하지 않는 코드를 가져오지 않습니다!
import { mergeAndConcat } from 'merge-anything'
mergeAndConcat (
{ nested : { prop : { array : [ 'a' ] } } } ,
{ nested : { prop : { array : [ 'b' ] } } }
)
// returns { nested: { prop: { array: ['a', 'b'] } } },
두 가지가 병합될 때 논리를 조정해야 하는 경우가 있을 수 있습니다. 값을 덮어쓸 때마다 트리거되는 고유한 사용자 지정 함수를 제공할 수 있습니다.
이 경우 mergeAndCompare
사용합니다. 다음은 문자열을 연결하는 비교 함수의 예입니다.
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' }
TypeScript 사용자를 위한 참고 사항입니다. 이 함수에서 반환된 유형이 올바르지 않을 수 있습니다. 이 경우 결과를 자신이 제공한 인터페이스로 캐스팅해야 합니다.
JavaScript 개체 참조에 주의하세요. 중첩된 모든 속성은 반응적이며 원본 개체와 병합된 개체 사이에 연결됩니다! 아래에서는 이를 방지하는 방법을 보여드리겠습니다.
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. ?'
기억해야 할 핵심 규칙은 다음과 같습니다.
겹치는 상위 속성 없이 2개 수준 이상 중첩된 모든 속성은 반응적이며 병합 결과와 소스 모두에 연결됩니다.
그러나 정말 쉬운 해결책이 있습니다 . 반응성을 제거하기 위해 병합 결과를 복사하면 됩니다. 이를 위해 우리는 copy-anything 라이브러리를 사용할 수 있습니다. 이 라이브러리는 또한 특수 클래스 인스턴스가 중단되지 않도록 하므로 문제가 발생할 염려 없이 사용할 수 있습니다!
아래에서 '무엇이든 복사'를 통합하는 방법을 참조하세요.
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
그런 다음 copy()
함수를 배치할 위치를 탐색할 수 있습니다.
Copy Anything은 TypeScript도 완벽하게 지원됩니다!
말 그대로 객체를 재귀적으로 처리하고 아래와 같이 새 객체에 값을 할당하는 것입니다. 그러나 추가 매개변수 등을 허용하도록 래핑되어 있습니다. 아래 코드는 기본 통합이므로 기본 작동 방식을 이해할 수 있습니다.
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 )
}
* 물론, 드문 경우 및 추가 기능에 대처하기 위해 실제 소스 코드와 약간의 차이가 있습니다. 실제 소스코드는 여기에 있습니다.