为什么我不能在 Typescript 中使用 switch 语句来缩小类类型?



带有注释的游乐场链接

这是使用interfaces进行类型缩小的标准示例。

// 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可以判断entity.height何时有效。

但现在这里有一个类似的例子,使用classes代替:

// 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函数的参数是MeatVeg,两者都具有不同的foodType属性。肉总是有foodType"Meat",素食总是有footType"Veg",所以把参数的foodType缩小到"Meat"不应该意味着食物参数应该有doneness属性吗?这似乎与上述示例中entity的缩小情况相同。实体参数是AnimalPlant,并且使用switch语句缩小参数的entityType

这两种缩小范围的情况有什么不同?为什么一种有效,而另一种无效?有没有一种方法可以使用classes做到这一点,并且仍然能够使用switch语句?

这是因为VegMeat的"foodType"被推断为类型FoodType。为了使其工作,您应该将Veg的foodType指定为foodType。VEG,肉的食物类型到食物类型。肉:

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;
}

最新更新