Issue
So here's my code
class Base {
constructor (public name : string) {}
}
class Extended extends Base {
get manged_name () {
return this.name;
}
}
function GENERATE<T extends typeof Base> (Model : T) : InstanceType<T> {
const instance = new Model("something");
return instance;
}
GENERATE(Extended);
note: GENERATE has been greatly simplified for example purposes
The error I get is Type 'Base' is not assignable to type 'InstanceType<T>'.
Expected
What I'm trying to do is allow any child classes of Base to be used as a parameter, and for its instance to be returned. Why is it erroring?
Solution
Inside your generate() function, the inferred type of new Model("something") is just Base. That's because accessing the construct signature on a generic constructor reduces that constructor to its constraint. See ms/TS#47502 for a source. So instead of being specific to T, the compiler has widened the instance type all the way to Base.
Furthermore, since the InstanceType<T> utility type is implemented as a conditional type like this:
type InstanceType<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: any) => infer R ? R : any;
and since the T type inside the body of your generate() function is a generic type parameter, that makes InstanceType<T> a conditional type that depends on a generic type parameter. And, alas, the TypeScript compiler can't really verify that anything is assignable to such types. They are essentially opaque to the compiler. See microsoft/TypeScript#48234, microsoft/TypeScript#48746 and undoubtedly many others for a source for that.
That means you are trying to assign the too-wide type Base to the opaque type InstanceType<T>, and it fails. If we want the compiler to be able to follow your logic, I'd recommend refactoring so that the generic type parameter corresponds to the instance type instead of the constructor type, like this:
function generate<T extends Base>(Model: new (name: string) => T): T {
const instance = new Model("something");
return instance; // okay
}
generate(Extended); // okay
Note how the Model parameter is given a construct signature which returns type T when called. Meaning that new Model("something") produces a value of type T, which is just what we want.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.