如何强制JSX组件可选属性组需同时提供或都不提供?
实现JSX组件属性的关联约束(link与title必须同时存在或缺失)
我有一个<Icon/> JSX组件,它可选择接受link和title属性,但要求当其中一个被提供时,另一个也必须被提供。现有基础代码如下:
interface IconProps { width?: number height?: number alt?: string src: string } interface IconWithLink { link: string title: string } declare function BetterIcon({ height, width, src, alt, ...rest }: BetterIconProps): any
尝试过的无效方案
我尝试了多种组合及继承原接口的方式,但都无法实现预期约束:
type BetterIconProps = IconProps | (IconProps & IconWithLink) type BetterIconProps = IconProps & (IconWithLink | {})
上述写法的问题是:智能提示会显示属性非可选,但实际未提供属性时不会触发TypeScript报错。
当前可行但扩展性差的方案
最终我通过以下方式实现了需求,但新增其他可选属性组时需要重复定义接口,扩展性差:
type BetterIconProps = IconWithoutLink | IconWithLink interface IconProps { width?: number height?: number alt?: string src: string } interface IconWithoutLink extends IconProps { link?: never title?: never } interface IconWithLink extends IconProps { link: string title: string }
是否有更通用、更简洁的实现方式?
更通用简洁的实现方式
可以通过封装通用关联属性工具类型来解决这个问题,避免重复定义多组接口,同时提升扩展性。
第一步:定义通用工具类型
这个工具类型的作用是:指定一组属性,要么同时传入(所有属性必填),要么同时不传(所有属性不能出现):
type LinkedProps<T> = T | { [K in keyof T]?: never };
第二步:组合最终的组件属性类型
基于原IconProps和需要关联的属性组,直接组合出BetterIconProps:
interface IconProps { width?: number; height?: number; alt?: string; src: string; } // 定义需要关联的属性对 interface LinkTitlePair { link: string; title: string; } // 组合得到最终属性类型 type BetterIconProps = IconProps & LinkedProps<LinkTitlePair>; declare function BetterIcon(props: BetterIconProps): JSX.Element;
效果验证
- 仅传入
link或仅传入title:TypeScript会抛出类型错误 - 同时传入
link和title:类型校验通过 - 两个属性都不传入:类型校验通过
扩展多组关联属性
如果后续需要新增其他关联属性组(比如target和rel必须同时存在或缺失),只需新增对应属性组并扩展BetterIconProps即可:
// 新增另一组关联属性 interface LinkTargetPair { target: string; rel: string; } // 扩展属性类型 type BetterIconProps = IconProps & LinkedProps<LinkTitlePair> & LinkedProps<LinkTargetPair>;
这种方式无需重复定义大量子接口,新增关联属性组的成本极低。
内容的提问来源于stack exchange,提问作者Qwerty
相关产品推荐
相关产品推荐

