在内部使用泛型方法

时间:2018-03-30 11:49:33

标签: typescript

为什么我会收到错误“在返回popupFactory(...)方法时,类型'AbstractPopup'不能赋值给T”?请忽略其他错误,这只是一个测试代码,可以更好地理解通用的工作方式。

function popupFactory<T extends AbstractPopup>(popupType: PopupType, data: {}): T 
{
    var popup: AbstractPopup;

    switch (popupType)
    {

        case PopupType.ConcretePopup:
        {
                popup = new ConcretePopup();
                break;
        }
    }

    return popup;
}

abstract class AbstractPopup
{
    type: PopupType;
}

class ConcretePopup extends AbstractPopup{}

var p = popupFactory<ConcretePopup>(PopupType.ConcretePopup, {});

2 个答案:

答案 0 :(得分:2)

泛型函数意味着它应该基于传递给它的泛型类型参数工作。在您的情况下,该方法基于枚举参数决定返回类型,而不是基于泛型参数。所以这个方法并不是通用的。

编译器抱怨的原因是,由于您可以传递来自T的任何AbstractPopup,因此无法确定您返回的类型是否与特定{{1}兼容你传递给函数。请考虑以下有效代码:

T

代码有效,但即使返回类型为class OtherConcretePopup extends AbstractPopup{} var p = popupFactory<OtherConcretePopup>(PopupType.ConcretePopup, {})

,也会返回ConcretePopup

最简单的解决方案是为每个弹出类型的方法设置几个重载:

OtherConcretePopup

或者你可以将构造函数作为参数传递,并且完全不需要切换,但只有当所有构造函数具有相同数量的参数且初始化对所有参数都相同时,这才是一个很好的选择:

function popupFactory(popupType: PopupType.OtherConcretePopup, data: {}): OtherConcretePopup
function popupFactory(popupType: PopupType.ConcretePopup, data: {}): ConcretePopup
function popupFactory(popupType: PopupType, data: {}): AbstractPopup 
{
    var popup: AbstractPopup = null;
    switch (popupType)
    {
        case PopupType.ConcretePopup:
        {
                popup = new ConcretePopup();
                break;
        }
    }

    return popup;
}

答案 1 :(得分:0)

您可以使用以下通用工厂方法创建任何派生类的实例,而不是使用开关。注意这里的new () => T签名,它表示没有参数构造函数的类型,所以如果它有一个默认的无参数构造函数,你可以在这里直接传递派生类名 -

function popupFactory<T extends AbstractPopup>(popupType:  new () => T, data: {}): T 
{
    var newPopup: T;
    newPopup = new popupType();
    return newPopup;
}

abstract class AbstractPopup
{
    //type: PopupType;
}


class ConcretePopup extends AbstractPopup{  
}

var p = popupFactory(ConcretePopup, {}); // creates instance of ConcretePopup class 

但是如果你的派生类在构造函数中有1个或多个参数,并且你需要在创建类的实例时设置参数值,你可以遵循以下方法 -

function popupFactory<T extends AbstractPopup>(popupType:  new (obj:Object) => T, data: {}): T 
{
    var newCust: T;
    newCust = new popupType(data);
    return newCust;
}

abstract class AbstractPopup
{
    //type: PopupType;
}


class ConcretePopup extends AbstractPopup{
    public fname: string;
    public lname: string;
    constructor(obj:Object) {
        super();
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (key == "fname") {
                    this.fname = obj["fname"];
                }
                if (key == "lname") {
                    this.lname = obj["lname"];
                }
            }
        }       

    }
}
var p = popupFactory(ConcretePopup, {"fname":"Niladri","lname":"D"});
console.log(p.fname); // Niladri
console.log(p.lname); // D

这里我传递popupType: new (obj:Object) => T作为在其构造函数中以Object为参数的类型。因此,我们可以将所需的属性和值作为对象传递,此处由data参数完成。但是这种方法的缺点是您必须手动提取派生类的属性,如上面的ConcretePopup类构造函数中所示。

这是一个工作小提琴的链接

Code sample