是否可以在 TypeScript 中创建严格的“A 或 B”类型? [复制]
Posted
技术标签:
【中文标题】是否可以在 TypeScript 中创建严格的“A 或 B”类型? [复制]【英文标题】:Is it possible to create strict 'A or B' types in TypeScript? [duplicate] 【发布时间】: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 中启用)在这里被击败。
为什么将kind
in circle 定义为可选?你的类型const shape: Shape = radius: 7;
是有效的圈子。为什么?因为它具有所有必需的道具(种类是可选的)
如果在Circle
中设置kind
,问题就消失了,因为shape
不再是有效的Shape
(它既不是Square
也不是Circle
) :playground
形状最好由一个区域来定义。然后方形和圆形就可以实现自己的计算了。我移植了一次go interface example,它与blog post 中的形状有关。
请参阅the answer 其他问题以获取更多信息。如果您愿意,可以使用ExclusifyUnion<T>
将具有已知键的对象类型的联合转换为一个新的联合,其中联合的成员是互斥的。如果我将其用于您的示例,它将变为this code。祝你好运!
【参考方案1】:
请注意,您已将kind
属性定义为可选属性,因此如果您创建的形状对象只有radius
属性仍然有效circle
:
看看这个对象
const shape: Shape =
sideLength: 7,
radius: 7,
;
这是具有一个额外属性的有效圆圈。
因此,在您所说的某些情况下要引发错误,您应该将 kind
属性设置为必需属性:
interface Circle
kind: "circle";
radius: number;
interface Square
kind: "square";
sideLength: number;
type Shape = Circle | Square;
const shape2: Shape =
radius: 7,//Error
;
PlaygroundLink
【讨论】:
“这是具有一个额外属性的有效圆圈。” 确实,尽管正如我在 cmets 中所说,TypeScript 通常对对象文字的多余属性检查将不允许这样做shape: Circle
而不是 shape: Shape
(即使它是有效的 Circle
)。
感谢您的回复。我刚刚更新了这个问题以回应这个问题,一些 cmets 详细说明了为什么(在我的用例中)我希望 kind
是可选的。以上是关于是否可以在 TypeScript 中创建严格的“A 或 B”类型? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何像在 babelrc 中创建依赖别名一样在 typescript 中创建类型别名?
我可以在 TypeScript 中创建一个对每个键值对都是类型安全的键值字典吗?