I have a factory decorator class taking an initializer function as an argument. In this initializer function, I would like to return an instance corresponding to the involved class type (or its derivative):
function JsonObject<T>(initializer: (json: any) => T) { return function (target: { new (...args: any[]): T }) {
@JsonObject(function (json) { return new Animal(); }) class Animal { name: string; }
Returning an instance of the exact class (as indicated above) works correctly, but ...
Short version
Returning an instance of a derived class fails. I can return the base instance, but not received. I cannot, for example, return a Cat :
@JsonObject(function (json) { return new Cat(); // Error. }) class Animal{ name: string; } class Cat extends Animal { color: string; }
... although Cat is an animal. However, I can return Animal instead of Cat (which is not true since Animal is not necessarily Cat) for Cat:
@JsonObject(function (json) { return new Animal();
Long version
JsonObject Factory Decoder
The JsonObject function JsonObject similar to a function with a parameter of general type T , which takes a callback function that returns T as an argument and returns a function that takes a new type that returns T Obviously, the latter (return function) is a decorator class .
The compiler will not let me - for example, return a string from this initializer function (or any other type of mismatch), which should be as it is.
Subtype Problem
However, the aforementioned type signature behaves in exactly the opposite way when subtypes are used: from the initializer function I can return a base type, but not a derived type - the following error occurs when a two-stage inheritance pattern is used in the middle class:
@JsonObject(function (json) {
Error: The type "Cat" cannot be assigned to the type "Kitty". The 'cutenessFactor' property is not present in the 'Cat' type.
I believe that I determined the origin of the error caused by the compiler when generalizing: a parameter of type T is derived from "T" in initializer: (json: any) => T , which means that the error is caused by a JsonObject function that has a common Kitty type, to which Cat , obviously, is not assigned as such, and therefore the class decorator cannot be used in Cat in this case.
I would like T to be deduced from the "return" target type instead, which would solve my problem. How could I do this?
Of course, when I explicitly specify the generic type parameter, it works flawlessly (but this contains redundant information):
@JsonObject<Cat>(function (json) { return new Kitty(); // OK, since type 'Kitty' is assignable to type 'Cat' }) class Cat extends Animal { }