admin管理员组文章数量:1332345
I got some typescript interface, abstract class and implementing sub-classes:
// 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;
}
}
As you can see the idea is that we have a Box
and some kind of Animal
in the box - in our example it can be Snake
or Owl
.
Now we can create Box
with Owl
inside.
let box = new Box( new Owl() );
And now the problem - using any method declared in superclass is pletely fine:
box.animal.sound(); // this is fine
but as you can see Owl have additional function fly()
and because fly is not declared in Animal
it throw:
box.animal.fly(); // Property 'fly' does not exist on type 'Animal'.
Also the same happens when creating normal variable:
let animal:Animal;
animal = new Owl();
animal.fly();
As addition Animal class do not have to be abstract, it can be normal class or interface - result will be the same.
My question is: why typescript throw it if my class is superset of other class. I think the main idea of interfaces and typing is guaranteeing that object has some properties like eat()
or sound()
in this example.
Im very new in typescript so it can be that I missed something, anyway how I can achieve that some variable must be some type but allowing additional methods in subclasses?
I got some typescript interface, abstract class and implementing sub-classes:
// 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;
}
}
As you can see the idea is that we have a Box
and some kind of Animal
in the box - in our example it can be Snake
or Owl
.
Now we can create Box
with Owl
inside.
let box = new Box( new Owl() );
And now the problem - using any method declared in superclass is pletely fine:
box.animal.sound(); // this is fine
but as you can see Owl have additional function fly()
and because fly is not declared in Animal
it throw:
box.animal.fly(); // Property 'fly' does not exist on type 'Animal'.
Also the same happens when creating normal variable:
let animal:Animal;
animal = new Owl();
animal.fly();
As addition Animal class do not have to be abstract, it can be normal class or interface - result will be the same.
My question is: why typescript throw it if my class is superset of other class. I think the main idea of interfaces and typing is guaranteeing that object has some properties like eat()
or sound()
in this example.
Im very new in typescript so it can be that I missed something, anyway how I can achieve that some variable must be some type but allowing additional methods in subclasses?
Share Improve this question asked Dec 14, 2017 at 16:18 GrivaGriva 1,73821 silver badges38 bronze badges 05 Answers
Reset to default 4Because typescript will not perform inference type for animal: Animal;
As you defined animal as an Animal
, so only methods and fields defined in Animal
will be available.
It is the way which strong typing works.
If you declare animal as :
animal
or
animal : any
You will be able to invoke any method on it but you lose the type checking.
As workaround, you could use a cast to manipulate a Owl
if the animal IS a Owl
.
Type assertions are a way to tell the piler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the piler. TypeScript assumes that you, the programmer, have performed any special checks that you need.
if (box.animal instanceof Owl){
(box.animal as Owl).fly
}
But a better way would be to have a generic Box
:
class Box<T extends Animal> implements BoxInterface {
animal: T
constructor(animal: T) {
this.animal = animal
}
}
Now you can write :
let box = new Box<Owl>(new Owl());
box.animal.sound()
box.animal.fly()
In any case, as IMSoP very well said : you have to know at some point that what you have is an Owl
if you want to apply method specific to Owl
.
Because Box
es are only guaranteed to have Animal
s in them. Not all Animal
s can fly()
.
You could cast (type assert) Box
's Animal
to an Owl
, and then have it fly:
(box.animal as Owl).fly()
You are right that a base class contract guarantees a minimum set of methods known to be available on all its sub-types.
However, TypeScript is enforcing an additional constraint here, that you should only invoke methods that you know are available on the object that you have.
In this case, all you know while you are writing the code box.animal.fly();
is that you have an Animal
; therefore, you should only call methods that all animals have.
Consider what would happen if this check was not there:
let animal:Animal;
animal = new Snake();
animal.fly();
You would get some kind of error at runtime. The idea of the type checker is to spot this possibility for you, and make you write code which is "safe" in this sense.
This is a OOP matter. When you declare a variable of a certain type and assign a value to it the value (sort of) has 2 types: a runtime type and a static (pile time) type. The type considered by the piler is the one declared for that variable (which is animal, not Owl and so can't have a method fly()
). This has implication on overriden methods (they could be resolved by static or dynamic information, depending on language specification). For more info look at
What is the difference between statically typed and dynamically typed languages? https://en.wikipedia/wiki/Multiple_dispatch
All other answers are valid. To your problem, you are best off using generics
class Box<T extends Animal> {
constructor(public animal: T) { }
}
const box = new Box(new Owl())
box.animal.fly()
本文标签: javascriptTypescript do not allow subclasses methods if variable is superclass typeStack Overflow
版权声明:本文标题:javascript - Typescript do not allow subclasses methods if variable is superclass type - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742326055a2453748.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论