带 TypeScript 的样式化组件 .attrs

IT技术 reactjs typescript styled-components
2021-04-30 00:08:21

我对如何在.attrs()TypeScript 中使用该函数感到有些困惑说我有以下几点:

BottleBar.tsx:

interface IBottleComponentProps {
  fill?: boolean
}

const BottleComponent = styled.div.attrs<IBottleComponentProps>(({fill}) => ({
  style: {
    backgroundImage: `url("./media/images/${fill ? 'Bottle-Filled.png' : 'Bottle-Empty.png'")`
  }
}))<IBottleComponentProps`
  width: 20px;
  height: 20px;
`;

export default function BottleBar() {

  return (
    <Wrapper>
      <BottleComponent />
      <BottleComponent fill />
    </Wrapper>
  )
}

现在,上面的代码有效,但我不确定为什么IBottleComponentProps在开头和结尾都需要两次 - 没有它,我得到以下内容:

Type '{ fill: boolean; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "slot" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "slot" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "slot" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.

此外,在第一个代码示例中,我得到了一个浏览器日志;

index.js:1 Warning: Received `true` for a non-boolean attribute `fill`.

老实说,这很令人困惑,而且 Styled-Components 文档在这方面不是很清楚。朝着正确的方向推动将不胜感激。

2个回答

答案 1: Warning about fill

您需要为您的样式组件选择一个不同的名称,也许是full,但不是fillAsfill是一些 HTML 元素的标准属性。此外,在 w3schools

实验:

如果您声明fillstring并传递一个字符串值,您可以看到HTML DOM 中fill添加了一个属性,例如:div

<div
  fill="test"
  style="background-image: url(&quot;/media/images/image_file.png&quot;);" class="sc-AxiKw jDjxaQ">
</div>

fill 是 SVGAttributes 接口中的一个属性:

来自node_modules/@types/react/index.d.ts

interface SVGAttributes<T> extends AriaAttributes, DOMAttributes<T> {
  // Attributes which also defined in HTMLAttributes
  className?: string;
  id?: string;
  ...
  // SVG Specific attributes
  accentHeight?: number | string;
  ...
  fill?: string;
  ...
}

这就是这个警告的原因:

Warning: Received `true` for a non-boolean attribute `fill`.
If you want to write it to the DOM, pass a string instead: fill="true" or fill={value.toString()}.

答案 2: Why interface is required 2 times?

以下是相关界面的摘录:

attrs<
      U,
      NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
          [others: string]: any;
      } = {}
  >(
      attrs: Attrs<StyledComponentPropsWithRef<C> & U, NewA, T>
  ): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;

U变成:IBottleComponentProps您传递的 C是 HTML 元素或react组件类型

返回类型是ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>

export interface ThemedStyledFunction<
    C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
    T extends object,
    O extends object = {},
    A extends keyof any = never

其中C,T已经提供。您提供O通过传递IBottleComponentProps第2次

如果您不提供它,您BottleComponent将看起来像下面{}的props,即没有props:

没有

如果您提供,它将看起来像下面一个,带有正确的props。

和

简而言之,您现在必须提供 2 次接口any如果您没有定义接口,您可以提供

看起来第二种类型的变量信息正在丢失第一种类型的信息。

这里是的定义 attr

export interface ThemedStyledFunction<
    C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
    T extends object,
    O extends object = {},
    A extends keyof any = never
> extends ThemedStyledFunctionBase<C, T, O, A> {
    // Fun thing: 'attrs' can also provide a polymorphic 'as' prop
    // My head already hurts enough so maybe later...
    attrs<
        U,
        NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
            [others: string]: any;
        } = {}
    >(
        attrs: Attrs<StyledComponentPropsWithRef<C> & U, NewA, T>
    ): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;

根据那个NewA类型变量应该有来自U类型的必要信息

然而结果是 ThemedStyledFunction<"div", any, {}, never>

理想情况下,它类似于 ThemedStyledFunction<"div", any, StyleProps & IBottleComponentProps, "style" | "fill">

type IBottleComponentProps = {
  fill?: boolean
}

type StyleProps = {
  style: {
    backgroundImage: string;
  }
}

const BottleComponent = styled.div.attrs<IBottleComponentProps, StyleProps & IBottleComponentProps>(({fill}) => ({
  style: {
    backgroundImage: `url("")`
  }
}))`
  width: 20px;
  height: 20px;
`;