问题描述
这是使用 interface
进行类型缩小的标准示例。
// 2 types of entity
enum EntityType {
ANIMAL = 'ANIMAL',PLANT = 'PLANT',}
// animal has animal type and has legs attribute
interface Animal {
entityType: EntityType.ANIMAL;
legs: number;
}
//plant has plant type and has height attribute
interface Plant {
entityType: EntityType.PLANT;
height: number;
}
// generic entity is animal or plant
type Entity = Animal | Plant;
// operate on an entity
const doEntityThing = (entity: Entity) => {
// can use type narrowing via switch based on entity.entityType
switch(entity.entityType) {
case EntityType.PLANT:
return entity.height;
case EntityType.ANIMAL:
return entity.legs;
}
};
在 switch 语句中,entity
的类型变窄了,因为 entity
可以成为的每个不同类型都有不同的 entityType
,所以 TS 可以判断什么时候,比如说,{{ 1}} 是否有效。
但现在这里有一个类似的例子,使用 entity.height
es 代替:
class
// 2 types of foods
enum FoodType {
MEAT = 'MEAT',VEG = 'VEG',}
// base class for generic food
class FoodBase {
public constructor(public foodType: FoodType){}
}
// instances of meat class have food type meat and have doneness attribute
class Meat extends FoodBase {
public static foodType = FoodType.MEAT;
public readonly foodType = Meat.foodType;
public constructor(public doneness: 'rare' | 'burnt') {
super(Meat.foodType);
}
}
// instances of veg class have food type veg and have organic attribute
class Veg extends FoodBase {
public static foodType = FoodType.VEG;
public readonly foodType = Veg.foodType;
public constructor(public organic: boolean) {
super(Veg.foodType);
}
}
// generic food is meat or veg
type Food = Meat | Veg;
// operate on a food
const doFoodThing = (food: Food) => {
// can use instanceof to narrow the type of the food
if(food instanceof Meat) {
console.log(`This meat is ${food.doneness}.`);
}
else if(food instanceof Veg) {
console.log(`This veg is${food.organic ? '' : ' not'} organic.`);
}
// can't use switch to narrow type! Why not?
switch(food.foodType) {
case FoodType.MEAT:
console.log(food.doneness); // ERROR HERE!
break;
case FoodType.VEG:
console.log(food.organic); // ERROR HERE!
break;
}
};
函数的参数是 doFoodThing
或 Meat
,两者具有不同的 Veg
属性。 Meat 总是有 foodType 'MEAT' 而 Veg 总是有 foodType 'VEG',所以不应该将参数的 foodType 缩小到 'MEAT' 意味着 food 参数应该有一个 foodType
属性吗?这似乎与上述示例中 doneness
的缩小情况相同。实体参数是 entity
或 Animal
,并且使用 switch 语句缩小参数的 Plant
范围。
这两种缩小情况有什么不同,为什么一种有效而另一种无效?有没有办法使用 entityType
es 执行此操作并且仍然能够使用 class
语句?
解决方法
这是因为 Veg
和 Meat
的 'foodType' 被推断为类型 FoodType
。为了使其工作,您应该将 Veg 的 foodType 指定为 FoodType.VEG,将 Meat 的 foodType 指定为 FoodType.MEAT:
class Meat extends FoodBase {
public static foodType = FoodType.MEAT as const;
// Or like this: public static foodType: FoodType.MEAT = FoodType.MEAT;
}
class Veg extends FoodBase {
public static foodType = FoodType.VEG as const;
// or like this: public static foodType: FoodType.VEG = FoodType.VEG;
}