TypeScript 接口向下转换

Posted

技术标签:

【中文标题】TypeScript 接口向下转换【英文标题】:TypeScript interface downcasting 【发布时间】:2016-08-17 00:37:56 【问题描述】:

我有一个正在扩展另一个接口的接口。现在我试图将超接口类型的变量放入需要子接口类型参数的函数中。这是不可能的,因为超接口类型变量缺少某些属性。

这有点难以解释,所以我创建了一个带有Animal 接口的示例,该接口由Horse 接口扩展:

interface Animal 
    age:number;
    //... 100 other things


interface Horse extends Animal 
    speed:number;

现在我有一个带有一个私有函数 (rideHorse) 的模块,它只接受一个 Horse 作为参数。还有一个公共函数 (rideAnimal),接受所有 Animals,如下所示:

module AnimalModule 
    function rideHorse(horse:Horse) 
        alert(horse.speed);
    

    export function rideAnimal(animal:Animal) 
        animal.speed = 10; // Error: Animal cannot have speed
        rideHorse(animal); // Error: 'animal' needs speed
    


// Calling the rideAnimal function
var myAnimal:Animal = 
    age: 10
;
AnimalModule.rideAnimal(myAnimal);

正如您所见,这不起作用,因为rideAnimalanimal 参数没有speed。所以我的问题是:如何将我的animal 转换为Horse 并在rideAnimal 函数中手动添加speed,这样错误就会消失?

【问题讨论】:

我不明白,您在下面的 cmets 中写道,所有动物都可以被认为是可以骑乘的,因此它们都需要速度。那么为什么不在 IAnimal 中定义速度呢? @Alex Animal 接口已经定义,无法更改,但我的问题不是这个。我只是想知道是否可以将超接口转换为子接口。 【参考方案1】:

你可以做任何你想做的事,因为 typescript 最终只是 javascript。但是你失去了静态类型的好处...... 例如,你可以写 (animal).speed = 12,但它会破坏编译时间检查...

从面向对象的角度来看,你不应该骑着没有速度的动物。

您应该添加一个中间接口“Irideable”扩展动物并由马继承。

【讨论】:

我知道在没有speed 的情况下不可能骑Animal,这就是为什么我想将speed 添加到animal 内的每个animal 对象rideAnimal 你会骑蠕虫吗?你的问题没有意义。您仍然可以将您的动物投射到任何动物并为其添加速度属性。但这是丑陋的设计 我明白你的意思,但在我的情况下,你可以假设animal 是可以骑的,但只是缺少speed 属性。很抱歉我的例子没有准确地反映现实世界。【参考方案2】:

我希望你的 RideAnimal 函数看起来像

export function rideAnimal<T extends Animal>(animal:T) 
    animal.speed = 10; // Error: Animal cannot have speed
    if (animal instanceof Horse) 
        rideHorse(animal as any as Horse); // Error: 'animal' needs speed
    

在使用抽象语法树时,这实际上是一种非常常见的模式,您会得到一个通用节点的引用,并且需要弄清楚它到底是什么类型。

一个完整的例子是:

class A 

class B extends A 
  doSomething() 


function test(a:A) 
  if (a instanceof B) 
    a.doSomething();
  

【讨论】:

虽然在我的情况下每个Animal 都应该变成Horse,那么这样的if 语句不是没有必要的吗?此外,这并不能消除错误。 更新了代码以使用泛型,希望能更健壮一点,但老实说,如果您正在寻找某些属性,您可能需要考虑结构类型。例如。仅当动物具有速度属性与动物所属的类别时才调用rideHorse 方法。【参考方案3】:

是的,您可以使用user defined type guards 来处理这种自定义类型评估:

interface Animal

    age: number;
    //... 100 other things


interface Horse extends Animal

    speed: number;


module AnimalModule

    function rideHorse(horse: Horse)
    
        alert(horse.speed);
    

    function isHorse(a: Animal): a is Horse
    
        return "speed" in a;
    

    export function rideAnimal(animal: Animal)
    
        if (isHorse(animal))
        
            animal.speed = 10; // Ok
            rideHorse(animal); // Ok
         else
            throw new Error("You can only ride horses")
    

如果HorseAnimal 是类,您可以只使用if (animal instanceof Horse) 而不是使用自定义类型保护isHorse

【讨论】:

【参考方案4】:

您可以像这样将Animal 转换为Horse

function rideAnimal(animal:Animal) 
    var horse = animal as Horse;
    horse.speed = 10;
    rideHorse(horse);

【讨论】:

这似乎是实现我想要的最简单的解决方案。虽然现在如果不将speed 添加到horse 将不会出现警告,所以speed 可以是undefined。但我想这是一个很小的代价。 如果您想要更安全,请使用转换函数toHorse,它接受Animal,将其转换为Horse,并在添加speed 后返回。 或者添加条件检查来检查动物是马(否则抛出)。我认为这是最好的方法。

以上是关于TypeScript 接口向下转换的主要内容,如果未能解决你的问题,请参考以下文章

向下转换接口参考

如何将 json 转换为 typescript 接口?

typescript - 将 json 转换为接口

30分钟学会TypeScript

VScode搭建TypeScript开发环境

将对象转换为 TypeScript 中的接口