【问题标题】:Typescript do not allow subclasses methods if variable is superclass type如果变量是超类类型,Typescript 不允许使用子类方法
【发布时间】:2017-12-14 16:18:34
【问题描述】:

我有一些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 - 在我们的示例中,它可以是SnakeOwl

现在我们可以在里面创建BoxOwl

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()

我在打字稿方面很新,所以可能是我错过了一些东西,无论如何我如何才能实现某些变量必须是某种类型但允许在子类中使用其他方法?

【问题讨论】:

    标签: javascript typescript inheritance typescript2.0


    【解决方案1】:

    因为 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。 p>

    【讨论】:

    • 但我的想法是我只知道这个对象将是 Animal 类型,但我不知道它是猫头鹰还是蛇。我只对有像吃这样的共享方法感兴趣,所以我不会出错,但如果 owl 有其他一些附加属性也没关系。这不是这个概念的重点吗?
    • 我明白了。使用泛型,您可以获得这种行为。我更新了。
    • 如果我从一些工厂/制造商那里得到这些对象,例如builder.createRandomAnimal();,我可以使用泛型吗?就像我后来确定这是猫头鹰或蛇,我运行了一些额外的方法
    • @Griva 你必须知道你有一个Owl,否则你不知道它可以fly。所以要么你有一个特殊的Box,它只能包含Owls(一个Box&lt;Owl&gt;),要么你检查对象并使用这个答案中解释的“类型断言”。另一种选择是说“我不关心类型检查,如果它不起作用就尝试给出运行时错误”;在这种情况下,TypeScript 不是您要查找的语言。
    • 为特殊的猫头鹰盒子投了赞成票,并且“你必须在某个时候知道你在 Owl 中拥有什么”。那是关键。我什至会将其添加到答案中。我认为这是一个新的 OOP 程序员并没有完全理解并期望语言只是推断它的障碍。
    【解决方案2】:

    因为Boxes 只能保证其中包含Animals。并非所有Animals 都可以fly()

    您可以将 (type assert) BoxAnimal 转换为 Owl,然后然后让它飞起来:

    (box.animal as Owl).fly()
    

    【讨论】:

      【解决方案3】:

      你是对的,基类契约保证了最少组已知在其所有子类型上可用的方法。

      然而,TypeScript 在这里实施了一个额外的约束,你应该只调用你知道在你拥有的对象上可用的方法。

      在这种情况下,您在编写代码box.animal.fly(); 时只知道您有一个Animal;因此,您应该只调用所有动物都有的方法。

      考虑如果没有这个检查会发生什么:

      let animal:Animal;
      animal = new Snake();
      animal.fly();
      

      您会在运行时遇到某种错误。类型检查器的想法是为您发现这种可能性,并让您编写在这个意义上“安全”的代码。

      【讨论】:

        【解决方案4】:

        这是一个面向对象的问题。当您声明某种类型的变量并为其赋值时,该值(某种意义上)有两种类型:运行时类型和静态(编译时)类型。编译器考虑的类型是为该变量声明的类型(它是动物,而不是 Owl,因此不能有方法 fly())。这对覆盖方法有影响(它们可以通过静态或动态信息解决,具体取决于语言规范)。欲了解更多信息,请查看

        What is the difference between statically typed and dynamically typed languages? https://en.wikipedia.org/wiki/Multiple_dispatch

        【讨论】:

          【解决方案5】:

          所有其他答案均有效。 对于您的问题,您最好使用泛型

          class Box<T extends Animal> {
            constructor(public animal: T) { }
          }
          
          const box = new Box(new Owl())
          box.animal.fly()
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-05-09
            • 1970-01-01
            • 2018-07-27
            • 2021-06-09
            相关资源
            最近更新 更多