在打字稿中不同实例的数组元素上调用方法?
Call method on the element of the array of the different instances in typescript?
我正在使用打字稿并遇到这样的问题。我有一个 classes 列表和它的实例数组。这些 classes 从一些基本的 class 扩展而来。每个 class 都有自己的方法。我想在数组的元素上调用这些自己的方法。但是我得到了一个错误。
这是一个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'.
我应该怎么做才能让它成为可能?
因为数组被推断为 BasicClass[]
类型,这实际上是正确的。现在 basic class 既没有 strangeName 也没有 specialName。在调用之前,您需要检查类型:
if(arr[1] instanceof Child2)
arr[1].getSpecialName()
您有两个选择:
1。您可能想要重构 class 层次结构,因此您可以调用一个通用方法,该方法将获得适合 class:
的特殊或奇怪或(等)名称
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 提供了两种转换方式;我将以上两个都用作示例。
好吧,在您的示例中,唯一的方法是为每个 项进行转换。如果你想为它们中的任何一个调用公共方法,你可以将你的数组定义为:
const array: Array<BasicClass> = [new Child1(), new Child2(), new Child3()];
array[1].getName(); // correct
但是,TypeScript 现在不知道特定索引的值为 class 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
)。
但是,假设我定义了两个新的 classes:
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
.
但是,如果您从Other
中删除getStrangeName
方法,您仍然可以将其分配给BasicClass[]
,因为它在结构上仍然与BasicClass
兼容。但是,将其分配给 array2
会失败,因为它与 Child1
、Child2
或 Child3
.
都不兼容
所以,归根结底,TypeScript 中的类型重要的是类型的 结构 ,而不是名称或它们是否相互派生。
我正在使用打字稿并遇到这样的问题。我有一个 classes 列表和它的实例数组。这些 classes 从一些基本的 class 扩展而来。每个 class 都有自己的方法。我想在数组的元素上调用这些自己的方法。但是我得到了一个错误。
这是一个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'.
我应该怎么做才能让它成为可能?
因为数组被推断为 BasicClass[]
类型,这实际上是正确的。现在 basic class 既没有 strangeName 也没有 specialName。在调用之前,您需要检查类型:
if(arr[1] instanceof Child2)
arr[1].getSpecialName()
您有两个选择:
1。您可能想要重构 class 层次结构,因此您可以调用一个通用方法,该方法将获得适合 class:
的特殊或奇怪或(等)名称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 提供了两种转换方式;我将以上两个都用作示例。
好吧,在您的示例中,唯一的方法是为每个 项进行转换。如果你想为它们中的任何一个调用公共方法,你可以将你的数组定义为:
const array: Array<BasicClass> = [new Child1(), new Child2(), new Child3()];
array[1].getName(); // correct
但是,TypeScript 现在不知道特定索引的值为 class 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
)。
但是,假设我定义了两个新的 classes:
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
.
但是,如果您从Other
中删除getStrangeName
方法,您仍然可以将其分配给BasicClass[]
,因为它在结构上仍然与BasicClass
兼容。但是,将其分配给 array2
会失败,因为它与 Child1
、Child2
或 Child3
.
所以,归根结底,TypeScript 中的类型重要的是类型的 结构 ,而不是名称或它们是否相互派生。