在typescript中的不同实例的数组元素上调用方法?

时间:2018-03-23 10:48:50

标签: javascript typescript oop ecmascript-6 typing

我正在使用打字稿并面临这样的问题。我有一个类列表和它的实例数组。这些类扩展自一些基本类。每个班级都有自己的方法。我想在数组的元素上调用这些自己的方法。但是我得到了一个错误。

这是example

class BasicClass { 
    getName() { 
        console.log('My name is BasicClass');
    }
}

class Child1 extends BasicClass { 
    getChildOneSpecialName() { 
        console.log('My getChildOneSpecialName is Child1');
    }
}

class Child2 extends BasicClass { 
    getSpecialName() { 
        console.log('My special name is Child2');
    }
}

class Child3 extends BasicClass { 
    getStrangeName() { 
        console.log('My getStrangeName is Child1');
    }
}


const array = [new Child1(), new Child2(), new Child3()];
array[1].getSpecialName(); // Property 'getSpecialName' does not exist on type 'Child1 | Child2 | Child3'.
array[2].getStrangeName(); // Property 'getStrangeName' does not exist on type 'Child1 | Child2 | Child3'.

我该怎么做才能成功?

3 个答案:

答案 0 :(得分:2)

嗯,在您的示例中,唯一的方法是为每个项目进行投射。如果要为其中任何一个调用公共方法,可以将数组定义为:

const array: Array<BasicClass> = [new Child1(), new Child2(), new Child3()];
array[1].getName(); // correct

但是,TypeScript现在还不知道特定索引的值为Child1类,因此它不允许您调用该方法。在你的情况下,你最好每次都投射:

(<Child2>array[1]).getSpecialName()

或者您可以将数组定义为any[]

const array: any[] = [new Child1(), new Child2(), new Child3()];
array[1].getSpecialName(); // correct

这可以让你根据自己的责任做任何你想做的事,但你也失去了编译器的帮助。

从谈话到T.J我将在这种情况下指出一些关于类型推断的问题。

作为T.J.指出,实际上不需要将array声明为Array<BasicClass>(或BasicClass[],这是相同的)。如果你不这样做,TypeScript会将数组的类型设为(Child1 | Child2 | Child3)[]

在这种情况下,如果你array[0].getName()它运作良好。在这两种情况下,TypeScript都知道数组的每个元素都有一个方法getName。如果你没有指定数组的类型,它知道因为Child1Child2Child3有这样的方法(不是因为它们扩展BasicClass )。

但是,让我们想象一下我定义了两个新类:

class Child4 extend BasicClass {
    getAnotherName() {
        console.log('anything');
    }
}

class Other {
    getName() { console.log('other'); }
    getStrangeName() { }
}

现在我们以两种方式定义数组:

const array1: BasicClass[] = [new Child1(), new Child2(), new Child3()];
const array2 = [new Child1(), new Child2(), new Child3()];
array1[0].getName(); // fine
array2[0].getName(); // fine

但是,让我们这样做:

array1.push(new Child4()); // fine. Child4 is a subclass of BasicClass
array1.push(new Child4()); // error! Child4 is not compatible with (Child1 | Child2 | Child3)!

如果我们这样做:

array1.push(new Other()); // Works! Why??
array2.push(new Other()); // also works.

如果您想知道为什么最后一个示例在两种情况下都有效,那是因为OtherBasicClass兼容(它的所有方法和属性都相同)签名)。它还与(Child1 | Child2 | Child3)兼容,因为它在结构上与Child3兼容。

但是,如果您从getStrangeName移除Other方法,您仍然可以将其分配给BasicClass[],因为它仍然与BasicClass在结构上兼容。但是,将其分配给array2会失败,因为它与Child1Child2Child3都不兼容。

因此,最后,TypeScript中类型的重要性在于类型的结构,而不是名称或它们是否相互派生。

答案 1 :(得分:1)

因为数组被引用到实际上正确的类型BasicClass[]。现在基本类既没有strangeName,也没有specialName。在你打电话之前,你需要检查类型:

 if(arr[1] instanceof Child2)
   arr[1].getSpecialName()

答案 2 :(得分:1)

您有几个选择:

1。您可能希望重构该类层次结构,因此您可以调用一个常用方法来获取适合该类的特殊或奇怪或(等)名称:

class BasicClass { 
    getName() { 
        console.log('My name is BasicClass');
    }
    getCustomName() {
        return this.getName();
    }
}

class Child1 extends BasicClass { 
    getCustomName() { 
        console.log('My getChildOneSpecialName is Child1');
    }
    // Or keep `getChildOneSpecialName` and have `getCustomName` call it
}

class Child2 extends BasicClass { 
    getCustomName() { 
        console.log('My special name is Child2');
    }
    // Or keep `getSpecialName` and have `getCustomName` call it
}

class Child3 extends BasicClass { 
    getCustomName() { 
        console.log('My getStrangeName is Child1');
    }
    // Or keep `getStrangeName` and have `getCustomName` call it
}


const array = [new Child1(), new Child2(), new Child3()];
array[1].getCustomName();
array[2].getCustomName();

您还可以向array添加类型注释:

const array : Array<BasicClass> = [new Child1(), new Child2(), new Child3()];

...但是TypeScript happily infers it,虽然它可能推断Child1 | Child2 | Child3而不是BasicClass

2。或者,根据需要检查类型和演员,但这通常不是最佳做法:

const array = [new Child1(), new Child2(), new Child3()];
if (array[1] instanceof Child2) {
    (array[1] as Child2).getSpecialName();
}
if (array[2] instanceof Child3) {
    (<Child3>array[2]).getStrangeName();
}

请注意,TypeScript提供了两种投射方式;我以上两个都是例子。