class Class {
}
const f0 = <T extends typeof Class> (c:T): T => {
return c
}
const call0 = f0 (Class) //ok
const f1 = <T extends typeof Class> (c:T): T => {
const a = new c()
return a //TS2322: Type 'Class' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Class'.
}
const call1 = f1 (Class)
const f2 = <T extends typeof Class> (c:T):InstanceType<T> => {
const a = new c()
return a //TS2322: Type 'Class' is not assignable to type 'InstanceType '.
}
const call2 = f1 (Class)
The argument is typed as T, so why isn't that acceptable for the return type?
CodePudding user response:
Your first snippet doesn't make a lot of sense. It says that it takes something that is a constructor that returns a Class
instance. That T
extends new () => Class
;
It also says it returns the same T (a constructor), but you are returning an instance; that doesn't make sense.
Solution
You were a lot closer in your second snippet
const f2 = <T extends typeof Class> (c:T): InstanceType<T> => {
const a = new c()
return a //TS2322: Type 'Class' is not assignable to type 'InstanceType '.
}
Again, typeof Class
expands to {new() => Class}
but what you really want is a generic constructor, so you can't just use typeof Class
, you have to write your own type for the generic constructor of Class
. Then your return type is a lot simpler.
class Class { a = 1; }
class SubClass extends Class { b = 2; }
class OtherClass { c = 3; }
type ClassCtor<T extends Class> = new () => T;
const f = <C extends Class> (c: new() => C): C => {
return new c();
}
// OR
const f = <C extends Class> (c: ClassCtor<C>): C => {
return new c();
}
// It's a Class instance
const call1 = f(Class);
// It's a SubClass instance
const call2 = f(SubClass);
// Fails compilation
const call3 = f(OtherClass);
See live example