When discussing classes, it’s important to note that they function as both values (the class constructor) and types.
So, if the value is represented by UserManager
as the class constructor, then typeof UserManager
delineates the type associated with that constructor. This type is used in conjunction with the new
keyword and includes static methods.
The type, on the other hand, exemplified by UserManager
, serves as the interface for instances.
The purpose of InstanceType<T>
is to identify the type of instances generated by a given constructor T
. For this reason, T
must be a constructor type to maintain validity. Otherwise, you’ll encounter a type error:
type A = InstanceType<UserManager>
// Type 'UserManager' does not satisfy the constraint 'abstract new (...args: any) => any'.
// Type 'UserManager' provides no match for the signature 'new (...args: any): any'.(2344)
This elucidates that the type:
InstanceType<typeof UserManager>
Is essentially indistinguishable from:
UserManager
Why can't we use a class as the type instead of needing to extract the type using typeof?
There's actually no requirement to do so unless solely possessing the type of a constructor. In the example shared, you can simply proceed with:
const user: UserManager = {
name: "John",
surname: "Doe",
};
The code functions appropriately because it adheres to the interface specifications of an instance belonging to UserManager
, regardless of its origin. Thus, Typescript accepts it without issue.
Update:
Referencing this playground link, triggers the following error message:
Argument of type 'typeof Child' is not assignable to parameter of
type 'Parent<{}>'. Property 'method' is missing in type
'typeof Child' but required in type 'Parent<{}>
"why is it labeled as typeof Child
?"
Each value
inherently adopts the type of typeof value
. Consequently, when utilizing Child
as a value (as demonstrated by passing it as an argument), its type automatically becomes typeof Child
.
Here, the value attributed to Child
represents a class constructor, essentially a callable function capable of generating instances of Child
.
"How can I make this work by only providing the class Child?"
To successfully execute instance methods tied to a class, instantiation is essential. Attempting to execute .method()
, an instance method, without creating an actual instance will inevitably fail.
Hence, your inquiry boils down to “How can I utilize an instance of a class sans instantiating it?” The answer is simple: you cannot.
Consider a car manufacturing facility. You cannot instruct the factory to ignite an engine; rather, you convey such instructions to a car produced by said factory. To initiate the car, you first need to construct one via the factory.
// The “factory” equivalent in this scenario
class Car {
startEngine() {}
}
const aCar: Car = new Car() // representative of what the factory manufactures
In summary:
You have the option to either create an instance prior to invoking the function:
function foo(instance: MyClass){
instance.someMethod()
}
foo(new MyClass())
Alternatively, you could transmit the constructor information and instantiate within the function:
function foo(someClass: typeof MyClass){
const instance = new someClass()
instance.someMethod()
}
foo(MyClass)
Ultimately, the bottom line remains - at some point, you must assemble the car to drive the car.