我正在使用打字稿并面临这样的问题。我有一个类列表和它的实例数组。这些类扩展自一些基本类。每个班级都有自己的方法。我想在数组的元素上调用这些自己的方法。但是我得到了一个错误。
这是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'.
我该怎么做才能成功?
答案 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
。如果你没有指定数组的类型,它知道因为Child1
,Child2
和Child3
有这样的方法(不是因为它们扩展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.
如果您想知道为什么最后一个示例在两种情况下都有效,那是因为Other
与BasicClass
兼容(它的所有方法和属性都相同)签名)。它还与(Child1 | Child2 | Child3)
兼容,因为它在结构上与Child3
兼容。
但是,如果您从getStrangeName
移除Other
方法,您仍然可以将其分配给BasicClass[]
,因为它仍然与BasicClass
在结构上兼容。但是,将其分配给array2
会失败,因为它与Child1
,Child2
或Child3
都不兼容。
因此,最后,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提供了两种投射方式;我以上两个都是例子。