I'm working on a factory and I need to eventually add custom methods. I'm able to not add the custom methods using overloads, but when I start adding the custom methods nothings works as desired. :(
I'm able to successfully cover the case where no custom methods are provided and the case where the custom methods argument has a wrong type.
type Base = { id: string }
type Methods<T> = { [key: string]: (this: T) => unknown };
function factory<T extends Base>(method: (this: T) => void): (new () => T);
function factory<
T extends Base & M,
M extends Methods<T>
>(method: (this: T) => void, methods: M): (new () => T);
function factory<
T extends Base & M,
M extends Methods<T>
>(method: (this: T) => void, methods?: M): (new () => T) {
return null as unknown as (new () => T);
}
// Ok: T = Base
factory(function() {
this.id = this.a();
});
// Ok: 0 is not valid value for argument methods (we don't care about T in this case)
factory(function() {
this.id = this.a();
}, 0);
While if I pass a valid value to the custom methods argument, nothing is working! The playground is a powerful tool check problems details.
// Nothing working as desired
factory(function() {
this.id = this.a();
}, {
a: function(): string {
this.a();
this.b();
return "0";
}
});
If we mouseover on the factory function name we can see its type is:
function factory<Base & Methods<unknown>, {
a: (this: Base & Methods<unknown>) => string;
}>
so we can say that
type T = Base & Methods<unknown>
type M = { a: (this: Base & Methods<unknown>) => string; };
here is the problem: since T is something, why M is resolved as Methods<unknown> rather than Methods<something>?
There are many other problems (the b method is not considered error, the a method is considered to return unknown rather than string as the a property of the Object passed as methods argument, etc.) but it seems they are all side effects of the wrong M type resolution.
I strongly suspect that the root cause of all these problems is the circular dependencies between T and M and between T and itself, but I can't do without because
Tneeds to depend onMto give it the custom methods;Mneeds to depend onTto infer the type of the implicit this argument to the custom methods.
Any idea about how implement this?