2016-04-23 60 views
2

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

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

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

interface Horse extends Animal { 
    speed:number; 
} 

现在我有一个私人的功能模块(rideHorse) ,它只接受Horse作为参数。和公共功能(rideAnimal),接受所有Animal就像如下:

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,这样错误就会消失?

+0

我不明白,下面的评论中写道,所有的动物都可以被认为是可骑乘的,因此他们都需要加速。那么你为什么不直接在IAnimal中定义速度? – Alex

+0

@Alex'Animal'界面已经定义好了,不能改变,但我的问题不在于此。我只想知道是否可以将超级接口投射到子接口。 –

+0

好吧,我想我会为你解答正确的答案... – Alex

回答

2

你可以投你Animal到这样一个Horse

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

这似乎是实现我想要的最简单的解决方案。虽然现在不会有任何警告,如果你不给'horse'加'speed',那么'speed'可以是'undefined'。但我认为这是一个小的代价。 –

+1

如果您想要更安全一些,可以使用一个转换函数'toHorse'来接受'Animal',将其转换为'Horse',并在添加一个'speed'后返回。 –

0

你可以做任何你想做的事情,因为打字稿只是JavaScript而已。但你失去了静态类型好处... 例如,CA写 (动物)。速度= 12,但它打破了编译时检查......

在视图中的面向对象的时候,你应该无法以无速度骑行的动物。

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

+0

我知道不应该可以在没有'speed'的情况下骑一只'Animal',这就是为什么我要为'rideAnimal'内的每个'animal'对象添加'speed'的原因。 –

+0

你可以骑蠕虫吗?你的问题没有意义。你仍然可以将你的动物施放到任何地方,并将速度属性添加到它。但这是丑陋的设计 –

+0

我明白你的意思,但在我的情况下,你可以假设'动物'是可以去除的,但是只是缺少'速度'属性。对不起我的例子没有准确地反映真实世界。 –

0

我希望你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(); 
    } 
} 
+0

尽管在我的情况下,每个“动物”都应该成为“马”,那么这样的if语句是不是不必要?此外,这并没有摆脱错误。 –

+0

已更新代码以使用泛型,希望稍微更健壮一些,但老实说,如果您正在寻找某些属性,您可能需要考虑结构化类型。例如。如果动物具有速度属性与动物属于哪个等级,则仅称为骑马方法。 – ArcSine

0

我已经找到一种方法,以通过创建HorseAnimal属性,而不是扩展它摆脱了错误的自己:

interface Horse { 
    animal:Animal; 
    speed:number; 
} 

而且将rideAnimal功能更改为:

export function rideAnimal(animal:Animal) { 
    var horse:Horse = { 
     animal: animal, 
     speed: 10 
    }; 

    rideHorse(horse); 
} 

它没有错误地工作,但看起来不正确,因为Horse应该Animal而不是有它。任何其他解决方案仍将不胜感激。

0

是的,你可以使用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