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
T
needs to depend onM
to give it the custom methods;M
needs to depend onT
to infer the type of the implicit this argument to the custom methods.
Any idea about how implement this?