【问题标题】:Typescript implement conditional props打字稿实现条件道具
【发布时间】:2021-04-30 11:27:55
【问题描述】:

我有以下组件,它可以包含图像或文本。因此在 JSX 中我检查是否有图像(因为这意味着不能有文本,反之亦然),然后显示图像或文本。

如何使用 TypeScript 类型定义这种行为?

BlockCtaDoubleData,始终可用,EITHER BlockCtaDoubleImageData OR BlockCtaDoubleTextData

我尝试了以下方法,但 Typescript 不断为 props.textprops.image 抛出错误,但对于 props.headline 却没有。

怎么样?

interface BlockCtaDoubleData
{
    headline: string;
}

interface BlockCtaDoubleImageData extends BlockCtaDoubleData
{
    image: string;
    alt: string;
}

interface BlockCtaDoubleTextData extends BlockCtaDoubleData
{
    text: string;
}

export type BlockCtaDoubleProps = BlockCtaDoubleTextData | BlockCtaDoubleImageData;

export function BlockCtaDouble(props: BlockCtaDoubleProps)
{
    return (
        <div>
            {props.headline}
            {props.image ? (
                <img src={props.image} alt={props.alt}/>
            ) : (
                {props.text}
            )}
        </div>
    );
}

仅供参考:错误表示计数器类型中不存在相应的道具。

【问题讨论】:

    标签: reactjs typescript types


    【解决方案1】:

    要区分BlockCtaDoubleImageDataBlockCtaDoubleTextData,您需要一个类型保护。

    import React from "react";
    
    interface BlockCtaDoubleData {
      headline: string;
    }
    
    interface BlockCtaDoubleImageData extends BlockCtaDoubleData {
      image: string;
      alt: string;
    }
    
    interface BlockCtaDoubleTextData extends BlockCtaDoubleData {
      text: string;
    }
    
    export type BlockCtaDoubleProps =
      | BlockCtaDoubleTextData
      | BlockCtaDoubleImageData;
    
    const isBlockCtaDoubleImageData = (
      a: BlockCtaDoubleProps
    ): a is BlockCtaDoubleImageData => a.hasOwnProperty("image");
    
    const isBlockCtaDoubleTextData = (
      a: BlockCtaDoubleProps
    ): a is BlockCtaDoubleTextData => a.hasOwnProperty("text");
    
    export function BlockCtaDouble(props: BlockCtaDoubleProps) {
      return (
        <div>
          {props.headline}
          {isBlockCtaDoubleImageData(props) ? (
            <img src={props.image} alt={props.alt} />
          ) : (
            props.text
          )}
        </div>
      );
    }
    

    类型保护是一个返回x is Type 的函数。您需要以这种方式实现它:如果 x 是 Type 则返回 true,否则返回 false。具体情况由您决定。

    编辑来自 cmets 的问题的答案

    你无法用你的方法实现它。您将不得不使用有区别的联合。

    
    interface BlockCtaDoubleData {
      headline: string;
      type: "image" | "text";
    }
    
    interface BlockCtaDoubleImageData extends BlockCtaDoubleData {
      image: string;
      alt: string;
      type: "image";
    }
    
    interface BlockCtaDoubleTextData extends BlockCtaDoubleData {
      text: string;
      type: "text";
    }
    
    export type BlockCtaDoubleProps =
      | BlockCtaDoubleTextData
      | BlockCtaDoubleImageData;
    
    export function BlockCtaDouble(props: BlockCtaDoubleProps) {
      return (
        <div>
          {props.headline}
          {props.type === "image" ? (
            <img src={props.image} alt={props.alt} />
          ) : (
            props.text
          )}
        </div>
      );
    }
    
    <BlockCtaDouble headline="as" text="xd" type="image" image="xd" />
    

    会抛出错误:

    Type '{ headline: string; text: string; type: "image"; image: string; }' is not assignable to type '(IntrinsicAttributes & BlockCtaDoubleTextData) | (IntrinsicAttributes & BlockCtaDoubleImageData)'.
      Property 'text' does not exist on type 'IntrinsicAttributes & BlockCtaDoubleImageData'.
    

    【讨论】:

    • 谢谢!这使它工作。还有一件事 - 调用组件,我仍然可以混合问题,我怎样才能让 TypeScript 告诉我,&lt; BlockCtaDouble headline="Test" image="some image" text="some text"&gt; 是不可能的。
    • 我想你需要一个通用组件,然后检查 TSX 是否可行。
    • @Tim 我已经添加了可能的解决方案。
    【解决方案2】:

    添加到现有答案,您可以使用更简洁的行为using in operator。您可以执行以下操作:

    interface BlockCtaDoubleData
    {
        headline: string;
    }
    
    interface BlockCtaDoubleImageData extends BlockCtaDoubleData
    {
        image: string;
        alt: string;
    }
    
    interface BlockCtaDoubleTextData extends BlockCtaDoubleData
    {
        text: string;
    }
    
    export type BlockCtaDoubleProps = BlockCtaDoubleTextData | BlockCtaDoubleImageData;
    
    export function BlockCtaDouble(props: BlockCtaDoubleProps)
    {
        return (
            <div>
                {props.headline}
                {("image" in props) ? (
                    <img src={props.image} alt={props.alt}/>
                ) : (
                    {props.text}
                )}
            </div>
        );
    }
    

    TS Playground to show the in behaviour

    【讨论】:

    • 谢谢,我怎么能另外告诉TS禁止调用像&lt;BlockCtaDouble headline="Test" image="some image" text="some text"/&gt;这样的组件(基本上只是再次混合类型)?
    • 作为structural typing,TypeScript 不会阻止您拥有额外的字段。因此,type T = {a : string}{a: 'adc', extra: 123} 兼容。在您的情况下,BlockCtaDoubleProps 只要包含headline, image, alt 属性就有效,如果您添加额外的属性名称text,它仍然有效BlockCtaDoubleProps。同理,如果包含headline, text键,则BlockCtaDoubleProps有效,但如果恰好包含image和/或alt作为额外键,则有效。
    • @Nishant 是对的。虽然你可以使用有区别的联合来实现它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 2019-05-22
    • 2018-06-30
    • 1970-01-01
    • 2021-07-11
    • 2018-06-04
    相关资源
    最近更新 更多