【问题标题】:Typescript: how to return the proper class and generic type in a dependencyTypescript:如何在依赖项中返回正确的类和泛型类型
【发布时间】:2017-09-27 01:50:23
【问题描述】:

我正在尝试找到一种方法将一个对象注入另一个对象(依赖注入),其中容器对象可以返回正确注入的对象类型,并且不需要显式地将类型传递给函数(类型推断)。
为确保注入的对象具有通用接口和默认行为,它们将是 Abstract 类的实例。

鉴于我需要在某些地方进行这样的行为,我想避免使用依赖注入框架来做到这一点。

在我看来,我需要在对象实例化时设置泛型类型,因为泛型参数之后无法重新定义。因此,我需要使用 factorynew() 接口)


这是一个例子。 Test it in Typescript Playground。为简单起见,我正在寻找 createMyClass2 以返回类型为 ArrayClass<string> 的对象,而无需在调用它时明确指定类型(类型推断)。
比如ArrayClassArrayClassAbstract可以认为是在不同的NPM模块中定义的某种插件。

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

好的泛型类型,错误的类类型...

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"

好的类类型,错误的泛型类型。 我不知道我声明工厂的方式是否有问题,或者我是否是 Typescript 限制的受害者......

有什么想法吗?

【问题讨论】:

    标签: typescript dependency-injection typescript-typings


    【解决方案1】:

    您只需要第二个类型参数。还是可以推断的:

    // 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
    

    【讨论】:

    • 您使用的是哪个版本的打字稿?对于 2.3,arr 被推断为 any[]:如果添加 let x: void = a.arr[0];,则不会出现错误,并且 Playground 在工具提示中显示 let a: ArrayClass&lt;any&gt;,而不是 ArrayClass&lt;string&gt;
    • @ryan-cavanaugh Errr... 正如@artem 所说,它不起作用。至少在 TS Playground 上。事实上,这种方法类似于我对第二个代码块“好的类类型,错误的泛型类型”所做的,除了我硬编码了string 类型而不是通过Inst 传递它(它返回any...)。我也是其他解决方案(如包装类)的购买者,但我觉得我尝试了一切......没有办法强制在父类或类似的东西中强制键入是隐式/自动的?使用中间函数?重新分配泛型?我不知道的事情会有所帮助吗?
    • 顺便说一下,为了提供更多上下文,我尝试编写一个对象链表(一个对象保存其父级),其中对象方法可以引用父级(并且它们都具有相同的基础类型)。这似乎是合法的,我没想到这样的模式会这么难输入。
    【解决方案2】:

    使用重载怎么样?

    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();
    

    【讨论】:

    • 这是关于依赖注入的
    • 糟糕!在您编辑答案之前无法更改我的投票...我可以为此 +1。虽然这对于工厂来说并不完美,而且我很难将其整合为最终解决方案,但这个想法还不错
    • @arvymetal 删除了不必要的类型。也许,您可以在问题中提供更多有关您需求的详细信息?
    • 你说得对,我认为我的问题不是很清楚。我刚刚尝试改进它。我不想提供太多上下文,我认为这会更令人困惑,感谢您的关注;)
    猜你喜欢
    • 2019-06-27
    • 2023-02-10
    • 1970-01-01
    • 2020-08-21
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2021-03-24
    • 1970-01-01
    相关资源
    最近更新 更多