Reputation:
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.
Upvotes: 11
Views: 8777
Reputation: 19813
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
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("/media/images/image_file.png");" 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()}.
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.
Upvotes: 13
Reputation: 11283
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;
`;
Upvotes: 1