Skip to content

Instantly share code, notes, and snippets.

@jtmthf
Created September 25, 2017 15:16
Show Gist options
  • Save jtmthf/8c40b69e9345c5a7d1f107f3a6cc26a9 to your computer and use it in GitHub Desktop.
Save jtmthf/8c40b69e9345c5a7d1f107f3a6cc26a9 to your computer and use it in GitHub Desktop.
Object.observe Proxy polyfill
type ChangeType = 'add' | 'update' | 'delete' | 'reconfigure' | 'setPrototype' | 'preventExtensions';
interface ChangeObjectBase<T> {
object: T;
type: ChangeType;
}
interface ChangeObjectUpdateDelete<T> extends ChangeObjectBase<T> {
name: keyof T | '__proto__';
type: 'update' | 'delete' | 'setPrototype';
oldValue: T;
}
interface ChangeObjectAddReconfigure<T> extends ChangeObjectBase<T> {
name: keyof T | '__proto__';
type: 'add' | 'reconfigure' | 'preventExtensions';
}
interface ChangeObjectPreventExtensions<T> extends ChangeObjectBase<T> {
type: 'preventExtensions';
}
type ChanegObject<T> = ChangeObjectUpdateDelete<T> | ChangeObjectAddReconfigure<T> | ChangeObjectPreventExtensions<T>;
function observe<T extends object>(obj: T, callback: (changes: ChanegObject<T>[]) => void, acceptList: ChangeType[]
= ['add', 'update', 'delete', 'reconfigure', 'setPrototype', 'preventExtensions']) {
function accept(type: ChangeType) {
return acceptList.indexOf(type) !== -1;
}
const p = new Proxy(obj, {
set(target, key: keyof T, value) {
if (key in target) {
const oldValue = p;
target[key] = value;
if (acceptList.indexOf)
accept('update') && callback([{ name: key, object: p, type: 'update', oldValue }]);
} else {
target[key] = value;
accept('add') && callback([{ name: key, object: p, type: 'add' }]);
}
return true;
},
deleteProperty(target, key: keyof T) {
const oldValue = p;
delete target[key];
accept('delete') && callback([{ name: key, object: p, type: 'delete', oldValue }]);
return true;
},
defineProperty(target, key: keyof T, descriptor) {
Object.defineProperty(obj, key, descriptor);
accept('reconfigure') && callback([{ name: key, object: p, type: 'reconfigure' }]);
return true;
},
setPrototypeOf(target, prototype) {
const oldValue = p;
Object.setPrototypeOf(target, prototype);
accept('setPrototype') && callback([{ name: '__proto__', object: p, type: 'setPrototype', oldValue }]);
return true;
},
preventExtensions(target) {
Object.preventExtensions(target);
accept('preventExtensions') && callback([{ object: p, type: 'preventExtensions' }]);
return true;
}
});
return p;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment