To achieve this, you can utilize the provided code snippet available at the following playground link.
class Wrapper<T> {
static create<T>(template: T): Wrapper<T> & T {
const target = new Wrapper(template);
const source = template as any;
Object.keys(template).forEach(key => {
Object.defineProperty(target, key, {
get: () => source[key],
set: value => { source[key] = value }
})
});
return target as any;
}
private _template: T;
private constructor(template: T) {
this._template = template;
}
foo(): void { console.log('foo'); }
bar(): void { console.log('bar'); }
}
interface Person {
readonly firstName: string;
readonly lastName: string;
readonly birthday?: Date
}
// with explicit generic
const wrappedPerson = Wrapper.create<Person>({
firstName: "John",
lastName: "Smith"
});
wrappedPerson.foo();
console.log(wrappedPerson.firstName, wrappedPerson.lastName, wrappedPerson.birthday);
// with type inference
const wrappedObject = Wrapper.create({
firstName: "Al",
lastName: "Green"
});
wrappedObject.bar();
console.log(wrappedObject.firstName, wrappedObject.lastName);
The above code demonstrates the create
method creating dynamic accessors on the wrapper object by using Object.defineProperty
, which then delegates to the defined template.
Below is the corresponding JavaScript version:
class Wrapper {
constructor(template) {
this._template = template;
}
static create(template) {
const target = new Wrapper(template);
const source = template;
Object.keys(template).forEach(key => {
Object.defineProperty(target, key, {
get: () => source[key],
set: value => { source[key] = value; }
});
});
return target;
}
foo() { console.log('foo'); }
bar() { console.log('bar'); }
}
// with explicit generic
const wrappedPerson = Wrapper.create({
firstName: "John",
lastName: "Smith"
});
wrappedPerson.foo();
console.log(wrappedPerson.firstName, wrappedPerson.lastName, wrappedPerson.birthday);
// with type inference
const wrappedObject = Wrapper.create({
firstName: "Al",
lastName: "Green"
});
wrappedObject.bar();
console.log(wrappedObject.firstName, wrappedObject.lastName);