I'm working on a factory; I need to eventually add custom methods, hanks to this answer and this answer, we was able to make it work almost as expected.
Almost because it works only with methods without any required arguments; if we try to add a method with at least one required arguments, we get a compile error.
I tried adding a rest argument array both to the declaration of method
argument and M
type (see below) but it helps only when calling the methods.
(this: E & S, ...args: unknonwn[]) => unknown
type Base = { id: string }
type Factory<E> = new () => E;
function factory<E extends Base>(name: string): Factory<E>;
function factory<E extends Base, M extends Record<string, <S extends M>(this: E & S, ...args: unknown[]) => unknown>>(
name: string, methods: M & Record<string, ((this: E & M, ...args: unknown[]) => void)>
): Factory<E & M>;
function factory<E extends Base, M extends Record<string, <S extends M>(this: E & S) => unknown>>(
name: string, methods?: M
): Factory<E> {
const ret = function (this: Base) {
this.id = "0"
};
Object.defineProperty(ret, "name", { value: name });
if (methods) for (const method in methods) Object.defineProperty(ret.prototype, method, { value: methods[method] });
return ret as unknown as Factory<E>;
}
const T1 = factory("T1");
const t1 = new T1();
console.log(t1, t1.id);
const T2 = factory(
"T2",
{
foo: function (repeat: boolean) {
const ret = ! repeat;
if(repeat) this.foo(ret);
return ret;
}
},
);
const t2 = new T2();
console.log(t2, t2.id, t2.foo(true));
Here is a playground to experiment.