According to Typescript documentation, the correct type for specifying a class constructor is the following:
type Class = { new (...args: any[]): {} }
And if you try to index this type like this:
type ClassPrototype = { new (...args: any[]): {} }["prototype"]
// OR EVEN
type ClassPrototype = Class["prototype"]
Everything is fine with the compiler while if you try something like the following, an error occurs:
type PrototypeOf<T extends { new (...args: any[]): {} }> = T["prototype"] // <-- Type '"prototype"' cannot be used to index type 'T'.
And I am not sure why this happens. If I am able to index Class
with Class["prototype"]
why am I not when it is a generic <T extends Class>
with T["prototype"]
? The solution i have found temporarily is changing <T extends Class>
with <T extends { prototype }>
but it doesn't look clean.
I have included the context I want to use this in here: playground link
CodePudding user response:
A class constructor's prototype
property has a type that is just that class as a type.
An example to help with that word salad:
class Class {
methodA(): void {}
}
const a: Class = Class.prototype // fine
type Test = Class['prototype'] // error
Here the value Class
(the constructor) when called with new
produces a value of type Class
(an instance).
To be honest, I've no idea what typescript allows the runtime access of Class.prototype
and not the type of Class['prototype']
. (But it's probably a deliberate trade off between making dealing with classes nice, at the cost of of making prototypical patterns harder, since classes are more common in modern codebases than manually building up prototypes)
But given that the type Class['prototype']
doesn't work on a real class, then you probably shouldn't expect that exist on an abstract class constructor type either.
I'm guessing you probably want InstanceType<T>
here instead.
type ClassConstructor = { new (...args: any[]): ClassInstance }
type ClassInstance = { methodA(): void }
type A = ClassConstructor['prototype'] // type is any, so this is not useful anyway
type B = InstanceType<ClassConstructor> // type is ClassInstance
type C = InstanceType<ClassConstructor>['methodA'] // type is void