Hold the line for just a second ...
Recently, I needed a type definition for a function that takes a class as an argument and returns an instance of this class. When I came up with a solution, this question soon came to my mind.
In principle, using the new type, you can call the relationship between the class and its instance, which accurately and perfectly answers your question:
function DecorateClass<T>(instantiate: (...args: any[]) => T) { return (classTarget: { new(...args: any[]): T }) => { } }
Description
In TypeScript, any given new type can be defined with the following signature:
new(...args: any[]): any
This is similar to a new type (constructor function), which may or may not accept arguments and return any (instance). However, nothing says that it should be returned any - it can be a general type.
And since we have exactly what is returned from the constructor function (by type - the input class to which the decorator is applied) inside the type parameter, we can use this to determine the return type of the passed callback function.
I tested the decorator and it seems to work exactly as expected:
@DecorateClass((json: any) => { return new Animal(); // OK }) @DecorateClass((json: any) => { return Animal; // Error }) @DecorateClass((json: any) => { return "animal"; // Error }) class Animal { public Name: string; public Sound: string; }
This will actually invalidate my previous answer.
Edit: Inheritance
When inheritance is involved (for example: a derived type must be returned from instantiate ), assignability seems to be upside down: you can return a base type, but not a derived type.
This is because the return type from instantiate takes precedence over the "returned" classTarget type during a typical output type. The following question explores this specific issue: