我有一些 typescript 接口,抽象类和实现子类:
// Animal classes
abstract class Animal {
abstract sound(): string;
constructor(public name: string) {
}
eat(food: string): string {
return "I eat this now: " + food;
}
}
class Snake extends Animal{
constructor() {
super("Snake");
}
sound() {
return "Sssssss";
}
}
class Owl extends Animal{
constructor() {
super("Owl");
}
sound() {
return "Hu-huu";
}
// Owl can also fly!
fly() {
return "I can flyyyy";
}
}
// Box classes
interface BoxInterface {
animal: Animal;
}
class Box implements BoxInterface {
animal: Animal;
constructor(animal: Animal) {
this.animal = animal;
}
}
正如您所看到的,我们的想法是我们在框中有Box
和某种Animal
- 在我们的示例中,它可以是Snake
或Owl
。
现在,我们可以在Box
内创建Owl
。
let box = new Box( new Owl() );
现在问题 - 使用超类中声明的任何方法都完全没问题:
box.animal.sound(); // this is fine
但正如你所看到的,Owl还有其他函数fly()
,因为fly不会在Animal
中声明它抛出:
box.animal.fly(); // Property 'fly' does not exist on type 'Animal'.
创建普通变量时也会出现同样的情况:
let animal:Animal;
animal = new Owl();
animal.fly();
另外,Animal类不必是抽象的,它可以是普通的类或接口 - 结果也是一样的。
我的问题是:如果我的班级是其他班级的超集,为什么打字稿会抛出它。我认为接口和输入的主要思想是保证该对象在此示例中具有eat()
或sound()
等属性。
我是打字稿中的新手,所以我可能错过了一些东西,无论如何我可以实现某些变量必须是某种类型但允许在子类中使用其他方法?
答案 0 :(得分:4)
因为typescript不会执行animal: Animal;
由于您将动物定义为Animal
,因此只有Animal
中定义的方法和字段才可用。
这是强类型工作的方式。
如果您将动物声明为:
animal
或
animal : any
您可以在其上调用任何方法,但是您会丢失类型检查。
作为解决方法,如果动物是Owl
,您可以使用强制转换来操纵Owl
。
类型断言是告诉编译器的一种方式“相信我,我知道什么 我正在做。“类型断言就像在其他语言中的类型转换, 但不会对数据进行特殊检查或重组。它没有 运行时影响,纯粹由编译器使用。 TypeScript假定 你,程序员,你已经对你进行了任何特殊的检查 需要。
if (box.animal instanceof Owl){
(box.animal as Owl).fly
}
但更好的方法是使用通用Box
:
class Box<T extends Animal> implements BoxInterface {
animal: T
constructor(animal: T) {
this.animal = animal
}
}
现在你可以写:
let box = new Box<Owl>(new Owl());
box.animal.sound()
box.animal.fly()
无论如何,正如IMSoP所说:如果你想将特定方法应用于Owl
Owl >
答案 1 :(得分:2)
因为Box
es仅保证包含Animal
个。并非所有Animal
都可以fly()
。
您可以将{type assert)Box
Animal
投射到Owl
,然后让它飞起来:< / p>
(box.animal as Owl).fly()
答案 2 :(得分:1)
基类合同保证了最小方法已知在其所有子类型上可用,这是对的。
但是,TypeScript在此处强制执行其他约束,您只应调用您知道的方法在您拥有的对象上可用。
在这种情况下,您在撰写代码box.animal.fly();
时所知道的就是您拥有Animal
;因此,您应该只调用所有动物的方法。
考虑如果没有检查会发生什么:
let animal:Animal;
animal = new Snake();
animal.fly();
你会在运行时遇到某种错误。类型检查器的想法是为您发现这种可能性,并让您编写“安全”的代码。从这个意义上说。
答案 3 :(得分:0)
这是一个OOP问题。声明某个类型的变量并为其赋值时,值(sort)有2种类型:运行时类型和静态(编译时)类型。编译器考虑的类型是为该变量声明的类型(它是动物,而不是Owl,因此不能有方法fly()
)。这暗示了覆盖方法(它们可以通过静态或动态信息解决,具体取决于语言规范)。有关详细信息,请查看
What is the difference between statically typed and dynamically typed languages? https://en.wikipedia.org/wiki/Multiple_dispatch
答案 4 :(得分:0)
所有其他答案均有效。 对于您的问题,您最好使用泛型
class Box<T extends Animal> {
constructor(public animal: T) { }
}
const box = new Box(new Owl())
box.animal.fly()