Styled Components .attrs w/ TypeScript

限于喜欢 提交于 2020-08-26 07:48:41

问题


I'm a little confused on how to use the .attrs() function with TypeScript. Say I have the following:

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>
  )
}

Now, the above code works, but I'm unsure why IBottleComponentProps is needed twice, both at the beginning and the end - And without it, I get the following:

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"> & { ...; } & { ...; }'.

Additionally, with the first code example, I get a browser log as such;

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

It's honestly pretty confusing, and the Styled-Components documentation isn't very clear in this regard. A push in the right direction would be greatly appreciated.


回答1:


Answer 1: Warning about fill

You need to choose a different name, maybe full, but not fill for your styled component. As fill is a standard attribute of some HTML elements. Also, at w3schools

Experiment:

If you declare fill to be string and pass it a string value, you can see a fill attribute added to you to div in HTML DOM, example:

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

fill is a property in SVGAttributes interface:

from 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;
  ...
}

That's the reason of this warning:

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()}.

Answer 2: Why interface is required 2 times?

Below is the excerpt from related interface:

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 becomes : IBottleComponentProps which you pass C is HTML element or react component type

And the return type is 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

where C, T were already provided. You are providing O by passing IBottleComponentProps the 2nd time.

If you don't provide it your BottleComponent will look like below one with {} for the props i.e. no props:

If you provide, it will look like below one, with the right props.

In short, you have to provide the interface 2 times for now. You can provide any if you don't have your interface defined.




回答2:


Looks like second type variable information is losing information from the first type.

Here's the definition of 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>;

According to that NewA type variable should have necessary information from U type.

The result is however ThemedStyledFunction<"div", any, {}, never>

Ideally it would be similar to 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;
`;



来源:https://stackoverflow.com/questions/61979237/styled-components-attrs-w-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!