我正在尝试为react组件属性定义一个类型脚本。我的组件是一个基本按钮,可以接受icon
属性,也可以接受text
属性。它不能两者都有,但必须有一个。
我试着从一个基本的歧视联盟开始,但它并不像预期的那样工作:
interface TextButtonProps extends TypedButtonProps {
text: string
}
interface IconButtonProps extends TypedButtonProps {
icon: JSX.Element
}
export const Button = ({ onClick, ...props }: IconButtonProps | TextButtonProps): JSX.Element => {
//...
当我在其他地方使用该组件时,TS不会抛出任何错误:<Button icon={<IconClose />} text='test' uiVariant='default' />
我在网上找到一篇文章,描述了带有可选属性的接口,但 never works:
interface TextButtonProps extends TypedButtonProps {
text?: string
icon?: never
}
interface IconButtonProps extends TypedButtonProps {
icon?: JSX.Element
text?: never
}
如果icon
和text
同时存在,我对<Button>
的所有使用都会抛出错误。
为什么会这样呢?我对它的冗长并不感到兴奋--如果我添加更多的按钮类型,我必须将这些新属性添加到每个界面中。
我的第二个问题是,因为属性是 optional,我可以不定义**图标或文本 prop -记住,我需要确保一个或另一个存在。
是否有更清洁的解决方案可以满足我的需求?
4条答案
按热度按时间wnrlj8wa1#
您几乎已经完成了,如果需要一个而不是两个,只需将
never
属性标记为optional:如果TS像Flow一样增加了对对象类型的支持,那么也许你可以在没有可选的
never
属性的情况下得到你想要的错误。另一种选择是创建委托给底层
Button
的单独组件:那么TS的额外属性检查就可以工作了--在大多数情况下。重要的是要记住额外属性检查并不完美,它不能像精确对象类型那样保证可靠性。
zz2j4svz2#
但是在这里你不想要这样的区别性属性。
为什么会这样?
没有类型可分配给
never
,因此尝试用任何类型(never
或undefined
以外的类型)定义属性/键都是错误的。这有效地禁止传递具有该名称的已定义属性/键。
因为属性是可选的,所以我可以不定义图标或文本属性
正如Andy's answer中所解释的,只有被禁止的
never
类型属性应该是可选的,其他属性可以保留为必需的。它是多么冗长-如果我添加更多按钮类型,我必须将那些新属性添加到每个单独的接口。
我们可以创建一些helper类型来分解代码。我猜这就是LindaPaiste在他们对这个问题的评论中提到的,我会让他们描述自己的解决方案,因为这听起来是一个有趣的通用helper!
与此同时,针对您的具体情况,您可以首先构建一个禁止 * 所有 * 特定 prop 的类型,这样您就可以在每个按钮类型上使用它,省略特定 prop ,使它们成为法律的的:
Playground链接
eiee3dmh3#
您必须在两个接口中包含一个公共属性,该属性的值在每个接口中都不同。了解有关区分联合的详细信息
k5hmc34c4#
你不需要定义两个接口来设置你的组件将接收到的值,一个接口就足够了,声明你需要什么样的参数,如果图标是字符串类型的名称,也是字符串类型按钮的标题;然后声明不需要的值并在组件中初始化它们。