You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 09:27:04