带有 TypeScript 的样式化组件 .attrs

Posted

技术标签:

【中文标题】带有 TypeScript 的样式化组件 .attrs【英文标题】:Styled Components .attrs w/ TypeScript 【发布时间】:2020-09-10 17:21:57 【问题描述】:

我对如何将 .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 文档在这方面不是很清楚。非常感谢您朝着正确的方向前进。

【问题讨论】:

【参考方案1】:

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

这是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&lt;"div", any, , never&gt;

理想情况下,它类似于ThemedStyledFunction&lt;"div", any, StyleProps &amp; IBottleComponentProps, "style" | "fill"&gt;

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;
`;

【讨论】:

【参考方案2】:

答案 1:Warning about fill

您需要为您的样式化组件选择一个不同的名称,可能是full,但不是fill。因为fill 是一些 HTML 元素的标准属性。 Also, at w3schools

实验:

如果您将fill 声明为string 并传递一个字符串值,您可以看到在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 元素或反应组件类型

并且返回类型是ThemedStyledFunction&lt;C, T, O &amp; NewA, A | keyof NewA&gt;:

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

C, T 已经提供。您通过第二次传递IBottleComponentProps 来提供O

如果您不提供它,您的BottleComponent 将如下所示,其中 用于道具,即没有道具:

如果您提供,它将如下图所示,并带有正确的道具。

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

【讨论】:

如果这在任何地方都有记录,那就太好了。非常感谢您的解决方案和出色的解释。 仅供参考,现在也可以在github.com/DefinitelyTyped/DefinitelyTyped/issues/…找到 在github.com/styled-components/styled-components-website/pull/775开了一个PR

以上是关于带有 TypeScript 的样式化组件 .attrs的主要内容,如果未能解决你的问题,请参考以下文章

带有 Typescript 和 ThemeProvider 的样式化组件。啥是正确的类型?

包括对带有 typescript 的样式化组件主题的媒体查询

使用带有 typescript 的样式化组件“as”prop

使用带有Typescript的样式化组件,prop不存在?

如何在使用 TypeScript 的 create-react-app 中使用带有 babel 插件的样式化组件?

如何将样式化组件作为属性添加到 TypeScript 中的另一个样式化组件?