Creating makeWatchedObject function with overloading
type PropEventSource<T> = {
on<K extends string & keyof T>(eventName: `${K}Changed`, callback: (newValue: T[K]) => void): void;
};
type ReturnType<T1> = T1 & PropEventSource<T1>;
function makeWatchedObject<T1>(obj1: T1): ReturnType<T1>
function makeWatchedObject<T1, T2>(obj1: T1, obj2: T2): ReturnType<T1 & T2>
function makeWatchedObject<T1, T2, T3>(obj1: T1, obj2: T2, obj3: T3): ReturnType<T1 & T2 & T3>
function makeWatchedObject<T1, T2, T3>(obj1: T1, obj2?: T2, obj3?: T3): any {
const events = new Map<string, (a: any) => void>();
const on_: PropEventSource<T1 & T2 & T3> = {
on(eventName: string, callback: (newValue: any) => void): void {
events.set(eventName, callback);
}
}
const obj = { ...obj1, ...obj2, ...obj3, ...on_ };
const proxyObj = new Proxy(obj, {
set: function (target, property, value, receiver) {
const event = events.get(`${property as string}Changed`);
if (event) {
event(value);
return true;
}
return Reflect.set(target, property, value, receiver);
}
});
return proxyObj;
}
Usage
const personInfo = { id: 1, firstName: 'kenan', lastName: 'hancer', age: 36 };
const personAddress = { country: 'UK', city: 'London' };
const personContact = { email: 'kh@kh.com', phone: '077111-33-33' };
let person = makeWatchedObject(personInfo, personAddress, personContact);
// works! 'newName' is typed as 'string'
person.on("firstNameChanged", (newName: string) => {
console.log(`new name is ${newName.toUpperCase()}`);
});
// works! 'newAge' is typed as 'number'
person.on("ageChanged", (newAge: number) => {
if (newAge < 0) {
console.log("warning! negative age");
}
});
person.age = 34;