Typescript: how to return the proper class and generic type in a dependency

谁都会走 提交于 2020-01-05 02:47:05

问题


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?


回答1:


How about using overloads?

function createMyClass2<MYTYPE>(type: typeof ArrayClass, element: MYTYPE): ArrayClass<MYTYPE>;
function createMyClass2<MYTYPE>(type: typeof SomeOtherArrayClass, element: MYTYPE): SomeOtherArrayClass<MYTYPE>;
function createMyClass2(className, element) {
    return new className(element);
}

let b = createMyClass2(ArrayClass, "hop!");  // type: {ArrayClass<string>}
b.test();
let c = createMyClass2(ArrayClass, 123);  // type: {ArrayClass<number>}
c.test();
let d = createMyClass2(SomeOtherArrayClass, "hop!");  // type: {SomeOtherArrayClass<string>}
d.test();



回答2:


You just need a second type parameter. It can still be inferred:

// Renamed some stuff for clarity
type ConstructorFor<CtorArg, Inst> = { new(element: CtorArg): Inst; }; 
function createMyClass<CtorArg, InstType extends ArrayClassAbstract<CtorArg>>(ctor: ConstructorFor<CtorArg, InstType>, arg: CtorArg) { 
    return new ctor(arg);
}

let a = createMyClass(ArrayClass, "hop!");   // type: {ArrayClass<string>}
a.test(); // OK


来源:https://stackoverflow.com/questions/43688748/typescript-how-to-return-the-proper-class-and-generic-type-in-a-dependency

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!