Created
September 25, 2017 15:16
-
-
Save jtmthf/8c40b69e9345c5a7d1f107f3a6cc26a9 to your computer and use it in GitHub Desktop.
Object.observe Proxy polyfill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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