React中如何基于type条件强制校验不同TypeScript interface对应属性
解决方案
第一步:重构为带严格属性校验的可辨识联合类型
你当前的接口定义没有将heroStyling设为每个子接口的固定字面量标识,也没有对非所属属性做禁止传入的限制,调整后的接口定义如下:
export type HeroStyling = 'SimpleForm' | 'WithTickerHeader' | 'WithTagLineHeader' | 'WithRating' // 基础泛型接口,绑定heroStyling的具体类型 export interface AdaptiveHeroMainProps<T extends HeroStyling> { heroStyling: T cta: string ctaUrl: string image: FluidObject imageAlt: string } export interface WithTagLineHeader extends AdaptiveHeroMainProps<'WithTagLineHeader'> { headingTagLine: string heading: string leadParagraph: string bulletPoints?: string // 不属于当前类型的属性显式设为never,禁止传入 textCenter?: never reviewStars?: never } export interface WithTickerHeader extends AdaptiveHeroMainProps<'WithTickerHeader'> { textCenter: boolean leadParagraph?: string bulletPoints?: string headingTagLine?: never heading?: never reviewStars?: never } export interface SimpleForm extends AdaptiveHeroMainProps<'SimpleForm'> { textCenter: boolean heading: string leadParagraph?: string bulletPoints?: string headingTagLine?: never reviewStars?: never } export interface WithRating extends AdaptiveHeroMainProps<'WithRating'> { textCenter: true reviewStars?: boolean // 禁止传入其他类型的专属属性 headingTagLine?: never heading?: never leadParagraph?: never bulletPoints?: never } // 导出组件可用的联合props类型 export type AdaptiveHeroProps = WithTagLineHeader | WithTickerHeader | SimpleForm | WithRating
调整后:
- 每个子接口的
heroStyling为固定字面量,成为可辨识联合的标识,TS可根据heroStyling的值自动收窄props类型 - 非当前子接口的属性被显式声明为
never类型,只要传入不属于当前类型的属性,TS会直接抛出类型不兼容错误,解决多余属性不报错的问题
第二步:调整组件写法解决属性不存在报错
由于我们已经在接口中将非公共属性声明为对应类型 | undefined | never,最终联合类型中所有非公共属性的类型都会被收敛为对应类型 | undefined,直接解构和判断渲染不会再触发类型错误,组件写法调整如下:
const CampaignHero = ({ heroStyling, cta, ctaUrl, image, imageAlt, textCenter, reviewStars, headingTagLine, heading, leadParagraph, bulletPoints }: AdaptiveHeroProps): React.ReactElement => { return ( <section> {headingTagLine && ( <div dangerouslySetInnerHTML={{ __html: headingTagLine }} /> )} {heading && <h1>{heading}</h1>} {leadParagraph && <p>{leadParagraph}</p>} {bulletPoints && ( <div dangerouslySetInnerHTML={{ __html: bulletPoints }} /> )} <Button href={ctaUrl}>{cta}</Button> {reviewStars && ( <HeroRating /> )} <Img alt={imageAlt} fluid={image} loading="eager" /> </section> ) }
如果需要更严谨的类型收窄,可以新增类型守卫函数,根据heroStyling的值判断当前props的具体类型:
const isWithRating = (props: AdaptiveHeroProps): props is WithRating => { return props.heroStyling === 'WithRating' } // 组件内使用 if (isWithRating(props)) { // 此处props的类型自动收窄为WithRating }
内容的提问来源于stack exchange,提问作者rockon_d
相关产品推荐
相关产品推荐

