Consider this failing example:
function DecorateClass<T>(instantiate: (...params:any[]) => T){
return (classTarget:T) => { /*...*/ }
}
@DecorateClass((json:any) => {
//Purely example logic here, the point is that it have to return
//an instance of the class that the decorator runs on.
var instance = new Animal();
instance.Name = json.name;
instance.Sound = json.sound;
return instance;
})
class Animal {
public Name:string;
public Sound:string;
}
Here I want to constrain the anonymous function in the decorator to always return an instance of the class in question, but the above does not work since T is actually typeof Animal
and not Animal
.
In a generic function, is there anyway I can get type Animal
from the type typeof Animal
without being annoyingly verbose like explicitly defining all types like function DecorateClass<TTypeOfClass, TClass>(...)
?
Unfortunately, using typeof in the generic syntax is not supported, which was my best bet in trying to get the compiler to understand what I want:
function DecorateClass<T>(instantiate: (json:any) => T){
return (classTarget:typeof T) => { /*...*/ } // Cannot resolve symbol T
}
In Python, decorators can be either functions or classes. In both cases, decorating adds functionality to existing functions. When we decorate a function with a class, that function becomes an instance of the class. We can add functionality to the function by defining methods in the decorating class.
A decorator is simply a function that takes a function as an argument and returns yet another function. Here, when we decorate, multiply_together with integer_check, the integer function gets called. The multiply_together method gets passed as the argument to the integer_check function and returns inner.
A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression , where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.
Hold the line just for a second...
Recently I've needed a type definition for a function that takes in a class as an argument, and returns an instance of that class. When I came up with a solution, this question soon came to my mind.
Basically, using a newable type it is possible to conjure a relation between a class and its instance, which accurately and perfectly answers your question:
function DecorateClass<T>(instantiate: (...args: any[]) => T) {
return (classTarget: { new(...args: any[]): T }) => { /*...*/ }
}
In TypeScript, any given newable type can be defined with the following signature:
new(...args: any[]): any
This is analogous to a newable type (the constructor function) that may or may not take arguments and returns any
(the instance). However, nothing says it must be any
that is returned -- it can be a generic type as well.
And since we have exactly what is returned from the constructor function (by type-inferring the class the decorator is applied to) inside a generic type parameter we can use that to define the return type of the passed in callback function.
I've tested the decorator, and it seems to be working precisely 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 effectively invalidates my previous answer.
When inheritance is involved (eg.: a derived type is to be returned from instantiate
), assignability seems to be flipped: you can return a base type, but not a derived type.
This is because the returned type from instantiate
takes precedence over the "returned" type of classTarget
during generic type-inference. The following question examines this exact problem:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With