Skip to content

Instantly share code, notes, and snippets.

@Conaclos
Created September 18, 2016 16:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Conaclos/ff673cd7b9c08172bb2f7bc33db86204 to your computer and use it in GitHub Desktop.
Save Conaclos/ff673cd7b9c08172bb2f7bc33db86204 to your computer and use it in GitHub Desktop.
Function that joins conflict-free objects. It copies all (enum, non-enum, and symbol) properties.
/*
Copyright (c) 2016 Victorien ELVINGER
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
interface DeepJoinFunction {
<A, B> (a: A, b: B): A & B
<A, B, C> (a: A, b: B, c: C): A & B & C
<A, B, C, D> (a: A, b: B, c: C, d: D): A & B & C & D
<A, B, C, D, E> (a: A, b: B, c: C, d: D, e: E): A & B & C & D & E
}
/**
* Copy all (enum, non-enum, and symbol) properties from `aSource' into `aTarget'.
* @param aSource
* @param aSubject - mutable
*/
function copyAll (aSource: Properties, aSubject: Properties): void {
let props: Array<string | symbol> = Object.getOwnPropertyNames(aSource)
props = props.concat(Object.getOwnPropertySymbols(aSource))
for (let f of props) {
aSubject[f] = aSource[f]
}
}
/**
*
* e.g. prototypeChainOfUntil({}, [null]) returns [Object.prototype]
* prototypeChainOfUntil({}, [Object.prototype]) returns []
*
* @param aTarget
* @param aBounds - must contain at least one element of the prototype
* chain of `aTarget'.
* Note that `null' is the bottom prototype of all objects.
* In contrast, `Object.protype' is not:
* this is the case for `Object.create(null)'
$ @return Prototype chain of `aTarget' until to meet
* one prototype of `aBounds'. First item is the deeper prototype.
*/
function prototypeChainOfUntil (aTarget: Properties,
aBounds: Array<Properties | null>): Array<Properties> {
const proto = Reflect.getPrototypeOf(aTarget)
if (aBounds.indexOf(proto) === -1) {
return prototypeChainOfUntil(proto, aBounds).concat(proto)
} else {
return []
}
}
/**
* Each pair of objects (a, b) must be conflict-free
* (except for "constructor" property).
* Every properties (enum, non-enum, and symbol) are copied.
*
* @param aFirst
* @param aOthers
* @return Concatenation of `aFirst' and `aOthers'.
*/
const deepJoin: DeepJoinFunction =
(aFirst: Properties, ...aOthers: Properties[]): Properties => {
const result = Object.create(aFirst) // mutable
let includedProtos = prototypeChainOfUntil(aFirst, [null]).concat([null])
for (let item of aOthers) {
const protos = prototypeChainOfUntil(item, includedProtos)
for (let proto of protos) {
copyAll(proto, result)
}
copyAll(item, result)
includedProtos = includedProtos.concat(protos)
}
if (Object.hasOwnProperty.call(result, "constructor")) {
delete result.constructor
}
return result
}
export {DeepJoinFunction, deepJoin}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment