【发布时间】:2021-10-20 09:57:01
【问题描述】:
我在输入 React 组件时遇到了以前从未遇到过的问题。我已经简化了下面的问题。
interface Circle {
kind?: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
const shape: Shape = {
sideLength: 7,
radius: 7,
};
Link to TypeScript Playground containing the above
在上面的示例中,我希望 shape 变量声明会在 TypeScript 中引发错误,因为 Circle 和 Square 都没有 both sideLength 和 radius。定义的对象既不是圆形也不是方形,所以在我看来它不应该是一个有效的形状。
是否可以定义类型以使以下项目有效或错误(如标签所示)。
// Valid
const shape: Shape = {
kind: 'circle',
radius: 7,
};
// Valid
const shape: Shape = {
radius: 7,
};
// Valid
const shape: Shape = {
kind: 'square',
sideLength: 7,
};
// Error
const shape: Shape = {
sideLength: 7,
radius: 7,
};
// Error
const shape: Shape = {
sideLength: 7,
};
// Error
const shape: Shape = {
kind: 'square',
radius: 7,
};
// Error
const shape: Shape = {
kind: 'circle',
sideLength: 7,
};
编辑:
为了进一步说明,在我的用例中,kind 在 Circle 上是可选的。对于那些熟悉 React 的人来说,我要解决的实际问题是样式组件暴露的 as 属性。我想要一个组件接受一个可选的as prop,它允许用户将组件(默认为button)更改为链接(a)。如果用户指定 as="a",那么我希望 TypeScript 能够在用户尝试在现在的链接上使用特定于按钮的道具(例如,disabled)时进行兼容。 as 属性是可选的,因为我不希望所有实现者都必须通过它。在我上面的简化示例中,as 类似于kind,因此为什么kind 是可选的。
【问题讨论】:
-
“定义的对象既不是圆形也不是方形,所以在我看来它不应该是一个有效的形状。” 请记住,子类型仍然是它们的有效示例超类型。你的
shape是 一个有效的形状,因为它是一个有效的Circle,因为它有一个radius: number属性并且没有kind属性(所以kind属性与Circle)。它也有一个额外的属性,但这只是使它成为一个子类型,而不是一个无效的Circle。不过,我很感兴趣,对象文字的多余属性检查(在您的 Playground 中启用)在这里被击败。 -
为什么将
kindin circle 定义为可选?你的类型const shape: Shape = {radius: 7};是有效的圈子。为什么?因为它具有所有必需的道具(种类是可选的) -
如果在
Circle中设置kind,问题就消失了,因为shape不再是有效的Shape(它既不是Square也不是Circle) :playground -
形状最好由一个区域来定义。然后方形和圆形就可以实现自己的计算了。我移植了一次go interface example,它与blog post 中的形状有关。
-
请参阅the answer 其他问题以获取更多信息。如果您愿意,可以使用
ExclusifyUnion<T>将具有已知键的对象类型的联合转换为一个新的联合,其中联合的成员是互斥的。如果我将其用于您的示例,它将变为this code。祝你好运!
标签: javascript typescript