proxy-extend

Transparently extend any JS object, using ES6 Proxy.
Motivation
Given some existing JS value, you may want to add some information to this value without actually modifying the original. The simplest way to do so is to create a wrapper around the value:
const someValue = getValue();
const someValueAnnotated = {
value: someValue,
status: 'ready',
};
One drawback of using a wrapper object, is that the newly annotated value now has a different interface from the original. That means that any consuming code will need to know about the wrapper and "unwrap" it do anything with it.
Using ES6 Proxy, we can make the wrapper have the same interface as the original value, allowing us to pass the wrapped value to any consuming code without the consumer needing to know whether it has been proxied or not.
Usage
import ProxyExtend from 'proxy-extend';
const user = { name: 'John' };
const userExtended = ProxyExtend(user, { status: 'ready' });
userExtended.name;
({ ...userExtended });
userExtended.status;
To make sure that we do not conflict with any existing properties on the original value, it is useful to use a Symbol as the key of the annotation:
import ProxyExtend from 'proxy-extend';
const user = { name: 'John' };
const meta = Symbol('meta');
const userExtended = ProxyExtend(user, { [meta]: 'some metadata' });
userExtended.name;
userExtended[meta];
Limitations
Due to the nature of Proxy, we can only use an object as target value. This library supports any JS object, including plain objects, arrays, functions, and class constructors. We also support a few kinds of primitives by emulating them using objects:
null (using an empty object with null prototype)
- Strings (using boxed
String)
- Numbers (using boxed
Number)
Checking reference equality will no longer work. That includes primitives as well:
const value = { x: 42 };
const proxy = ProxyExtend(value);
value !== proxy;
const proxyString = ProxyExtend('foo');
proxyString !== 'foo';
String(proxyString) === 'foo';
Similar libraries