Javascript 若变量为超类类型,则Typescript不允许子类方法
我得到了一些类型脚本接口、抽象类和实现子类:Javascript 若变量为超类类型,则Typescript不允许子类方法,javascript,typescript,inheritance,typescript2.0,Javascript,Typescript,Inheritance,Typescript2.0,我得到了一些类型脚本接口、抽象类和实现子类: // 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
// 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;
}
}
正如你所看到的,我们有一个盒子
,盒子里有某种动物
——在我们的例子中,它可以是蛇
或猫头鹰
现在,我们可以在Owl
中创建Box
let box = new Box( new Owl() );
现在问题来了-使用超类中声明的任何方法都是完全正确的:
box.animal.sound(); // this is fine
但正如您所见,猫头鹰有额外的函数fly()
,因为fly没有在Animal
中声明,所以它抛出:
box.animal.fly(); // Property 'fly' does not exist on type 'Animal'.
创建正常变量时也会发生同样的情况:
let animal:Animal;
animal = new Owl();
animal.fly();
由于添加的动物类不必是抽象的,它可以是普通类或接口-结果将是相同的
我的问题是:如果我的类是其他类的超集,为什么typescript会抛出它。我认为接口和类型的主要思想是保证对象具有一些属性,如本例中的eat()
或sound()
我对typescript非常陌生,所以我可能错过了一些东西,无论如何,我如何才能实现某些变量必须是某种类型,但允许在子类中使用其他方法?因为
Box
es中只保证有Animal
s。并非所有的动物都能飞()
你可以把盒子里的动物扔给猫头鹰,然后让它飞起来:
(box.animal as Owl).fly()
因为typescript不会对animal:animal执行推断类型代码>
由于您将动物定义为动物
,因此只有动物
中定义的方法和字段可用。
这是强打字的工作方式
如果您将动物声明为:
animal
或
您可以对其调用任何方法,但会丢失类型检查
作为解决方法,如果动物是猫头鹰
,则可以使用石膏来操纵猫头鹰
类型断言是告诉编译器“相信我,我知道什么
类型断言就像其他语言中的类型转换,
但不执行数据的特殊检查或重构。它没有
运行时影响,并且仅由编译器使用。类型脚本假定
您,程序员,已经执行了您需要的任何特殊检查
需要
但更好的方法是使用一个通用的框
:
class Box<T extends Animal> implements BoxInterface {
animal: T
constructor(animal: T) {
this.animal = animal
}
}
类框实现了BoxInterface{
动物:T
建造师(动物:T){
这个动物
}
}
现在你可以写:
let box = new Box<Owl>(new Owl());
box.animal.sound()
box.animal.fly()
let box=newbox(new Owl());
盒子。动物。声音()
box.animal.fly()
在任何情况下,正如IMSoP所说:您必须在某一点上知道,您拥有的是一个Owl
,如果您想应用特定于Owl
的方法,那么基类契约保证在其所有子类型上都有一组已知的最小方法是正确的
但是,TypeScript在这里强制执行了一个附加约束,即您应该只调用已知在您拥有的对象上可用的方法
在本例中,编写代码时所知道的一切box.animal.fly()代码>是指你有一只动物
;因此,您应该只调用所有动物都具有的方法
考虑一下如果没有这张支票会发生什么:
let animal:Animal;
animal = new Snake();
animal.fly();
在运行时会出现某种错误。类型检查器的想法是为您发现这种可能性,并让您编写在这种意义上“安全”的代码。这是一个面向对象的问题。当您声明某一类型的变量并为其赋值时,该值(排序)有两种类型:运行时类型和静态(编译时)类型。编译器考虑的类型是为该变量声明的类型(它是animal,不是Owl,因此不能有方法fly()
)。这意味着重写的方法(它们可以通过静态或动态信息来解决,具体取决于语言规范)。有关更多信息,请查看
所有其他答案均有效。
对于您的问题,最好使用泛型
class Box<T extends Animal> {
constructor(public animal: T) { }
}
const box = new Box(new Owl())
box.animal.fly()
类框{
构造函数(公共动物:T){}
}
常量框=新框(新Owl())
box.animal.fly()
但我的想法是,我只知道这个对象将是动物类型,但我不知道它是猫头鹰或蛇。我只对像eat这样的共享方法感兴趣,这样我就不会出错,但如果owl有一些其他附加属性,那也没关系。这不是这个概念的要点吗?我明白。使用generic可以获得这种行为。我更新了。如果我从某个工厂/构建器获取这些对象,例如builder.createRandomAnimal()代码>我可以使用泛型吗?这就像我后来确定这是猫头鹰或蛇,我运行一些额外的methods@Griva你必须在某一点上知道你拥有的是一只猫头鹰,否则你不知道它可以飞行。因此,要么您有一个特殊的框
,它只能包含Owl
s(一个框
),要么您检查对象并使用“类型断言”,如本答案中所述。另一种方法是说“我不关心类型检查,如果它不起作用,就尝试给出一个运行时错误”;在这种情况下,TypeScript不是您要寻找的语言。投票支持特殊的Owl框,并且“您必须在某个时刻知道您在Owl
中拥有什么”。这是关键。我甚至会在答案中加上这一点。我认为这是一个新的OOP程序员没有完全理解的障碍,并期望语言能够推断出它。
let animal:Animal;
animal = new Snake();
animal.fly();
class Box<T extends Animal> {
constructor(public animal: T) { }
}
const box = new Box(new Owl())
box.animal.fly()