typescript - 类型保护可以应用于接口的成员吗?
问题描述
我有以下代码:
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
)。
IOnlyLikeApples
有一个不同于switch
.
解决方案
在尝试将开关用作类型保护之前,我遇到过这个问题。这取决于打字稿在开关开始时将类型分配给对象o
并且从不重新评估它的事实。如果您查看分支的o
和o.fruit
内部的值case Fruit.Apple
,您会看到 TS 知道它o.fruit
是 type Fruit.Apple
,但仍然认为 typeo
是FruitWrapper
并且没有添加任何其他内容。
有很多方法可以解决这个问题。最简单也是最草率的做法是简单地断言你所知道的:IOnlyLikeApples(o as FruitWrapper & { fruit: Fruit.Apple });
你必须使用switch语句吗?如果您可以在 an 内使用类型保护重写它,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;
}
}
推荐阅读
- php - Laravel 加入没有正确返回数据
- google-cloud-platform - 处理 Cloud Run 容器关闭
- mongodb - 为什么当 MongoDB 副本集分片成员之一下线时,其他 CPU 的峰值达到 100%?
- c# - 从 PSCmdlet 类 (C#) 在主机 Powershell 中调用原始命令
- c# - 先前工作代码中带有语句体错误的 Lambda 表达式
- python - 在 pytest 中,如何中止夹具拆卸?
- ckeditor - CKEditor 4 - 删除图像工具栏按钮导致图像对话框不再显示宽度和高度属性
- r - 传入列名作为参数
- javascript - 在 Javascript 中从父级返回子 ID 列表
- jquery - 除非事先选择,否则 JQuery 试图在下拉菜单中隐藏一个选项