我有以下代码:
enum Fruit {
Apple,
Banana,
}
interface FruitWrapper {
fruit: Fruit;
//...
}
function IOnlyLikeApples(o: FruitWrapper & { fruit: Fruit.Apple }) { ... }
和一个switch
作为我的类型保护
const o: ISomeInterface = getFruit();
switch(o.fruit) {
case Fruit.Apple:
IOnlyLikeApples(o);
break;
}
我在将o
传递给IOnlyLikeApples
时遇到以下错误:
Argument of type 'FruitWrapper' is not assignable to parameter of type 'FruitWrapper & { fruit: Fruit.Apple; }'.
Type 'FruitWrapper' is not assignable to type '{ fruit: Fruit.Apple; }'.
Types of property 'fruit' are incompatible.
Type 'Fruit' is not assignable to type 'Fruit.Apple'.
TS为什么抱怨这件事?有一种观点认为,即使引用o
是常数,其成员也不是,但o.fruit
没有被重新分配。即使我使用interface FruitWrapper { readonly fruit: Fruit; }
,TS仍然拒绝此代码。
我能想到的唯一解决方案是以下内容,
const o: ISomeInterface = getFruit();
const f = o.fruit;
switch(f) {
case Fruit.Apple:
IOnlyLikeApples({ ...o, fruit: f });
break;
}
这是丑陋和恶心的(在这一点上,不值得编译器支持来帮助IOnlyLikeApples
的调用方(。
CCD_ 8与CCD_。
我有点工作,但不是那么漂亮:
const getFruit = () => {
if (Math.random() > 0.5) {
return {
fruit: Fruit.Banana,
misc: 'asd'
// the as const is needed here. Unfortunate.
} as const
} else {
return {
fruit: Fruit.Apple,
misc: 'asd'
// and here. Unfortunate again.
} as const
}
}
const o = getFruit();
switch(o.fruit) {
case Fruit.Apple:
IOnlyLikeApples(o);
break;
case Fruit.Banana:
IOnlyLikeApples(o);
}
游乐场链接
我以前在尝试使用开关作为类型保护时遇到过这个问题。这是因为typescript在切换开始时将类型分配给对象o
,并且从不重新计算它。如果您查看case Fruit.Apple
分支中的o
和o.fruit
的值,您会发现TS知道o.fruit
的类型是Fruit.Apple
,但仍然认为o
的类型为FruitWrapper
,并且没有向其添加任何其他内容。
有很多方法可以解决这个问题。最简单、最草率的是简单地断言你所知道的:IOnlyLikeApples(o as FruitWrapper & { fruit: Fruit.Apple });
你必须使用switch语句吗?如果您可以在if
内部使用类型保护来重写它,那么您就不会有这个问题。
type Apple = FruitWrapper & { fruit: Fruit.Apple };
const isApple = (o: FruitWrapper): o is Apple => {
return o.fruit === Fruit.Apple;
}
const handleFruit = ( o: FruitWrapper ) => {
if ( isApple(o) ) {
IOnlyLikeApples(o);
} else {
/*...*/
}
}
另一种选择是将水果作为并集类型,这取决于你的数据,可能有意义,也可能没有意义。switch
在接口内的指定方面做得不够,但它能够处理联合内的区分。这很好:
type Apple = FruitWrapper & { fruit: Fruit.Apple };
type Banana = FruitWrapper & { fruit: Fruit.Banana };
type FruitUnion = Apple | Banana;
const handleFruit = ( o: FruitUnion ) => {
switch(o.fruit) {
case Fruit.Apple:
IOnlyLikeApples(o);
break;
case Fruit.Banana:
/* ... */
break;
}
}
游乐场链接