I'm trying to find a way to inject an object into another object (dependency injection), in which the container object can return the proper injected object type, and no need to explicitly pass types to the function (type inference).
To make sure the injected objects have a common interface and a default behavior, they'd be instances of an Abstract class.
Given that I'd need such a behavior at some ponctual place, I would like to avoid using a dependency injection framework to do so.
It seems to me that what I need is to set the generic type on object instantiation because generic parameters cannot be redefined afterwards. Consequently I'd need to use a factory (
new()
interface)
Here is an example. Test it in Typescript Playground. To make things simple, I'm looking for createMyClass2
to return an object typed ArrayClass<string>
, without specifying the type explicitly when calling it (type inference).
It may be considered that ArrayClass
and ArrayClassAbstract
are some kind of plugins defined in different NPM modules for example.
class ArrayClassAbstract<MYTYPE> {
public arr: Array<MYTYPE> = []
constructor(element: MYTYPE) {
this.arr.push(element);
}
}
class ArrayClass<MYTYPE> extends ArrayClassAbstract<MYTYPE> {
test() {
return this.arr;
}
}
class SomeOtherArrayClass<MYTYPE> extends ArrayClassAbstract<MYTYPE> {
test() {
return this.arr + "something different";
}
}
function createMyClass<MYTYPE>
(className: { new <MYTYPE>(element: MYTYPE): ArrayClassAbstract<MYTYPE> }, element: MYTYPE) {
return new className<MYTYPE>(element);
}
let a = createMyClass(ArrayClass, "hop!"); // type: {ArrayClassAbstract<string>}
a.test(); // Doesn't work... "test" doesn't exist in ArrayClassAbstract
Good generic type, wrong class type...
function createMyClass2<T extends ArrayClassAbstract<string>>
(className: { new (element: string): T }, element: string) {
return new className(element);
}
let b = createMyClass2(ArrayClass, "hop!"); // type: {ArrayClass<any>}
b.test(); // Works but array elements typed as "any"
Good class type, wrong generic type. I don't know if there's something wrong in my way to declare the factory, or if I'm victim of Typescript limitations...
Any idea?
How about using overloads?
You just need a second type parameter. It can still be inferred: