user2239457
user2239457

Reputation: 61

Casting styled components in React + Typescript

I'm trying to implement animation in React + Typescript.

interface IImageProps {
    frame: number
    width: number
    src: string
    onLoad: () => void
}

const Image = styled.img`
    transform: ${(props: IImageProps) => css`translate(0, -${props.frame * props.width}px)`};
`

This throws a warning in console:

styled-components.browser.esm.js:1507 Over 200 classes were generated for component styled.img. 
Consider using the attrs method, together with a style object for frequently changed styles.

So I'm trying to use attrs:

const Image = styled.img.attrs((props: IImageProps) => ({
    style: { transform: `translate(0, -${props.frame * props.width}px)` },
}))``

now TS complains that:

Type '{ src: string; onLoad: () => void; width: number; frame: number; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<Pick<DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>, "src" | "width" | "children" | "style" | "title" | ... 255 more ... | "useMap"> & { ...; } & { ...; }, "src" | ... 259 more ... | "useMap"> & Partial<...>, "src" | ... 260 more ... | "useMap"> & { ...;...'.
  Property 'frame' does not exist on type 'IntrinsicAttributes & Pick<Pick<Pick<Pick<DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>, "src" | "width" | "children" | "style" | "title" | ... 255 more ... | "useMap"> & { ...; } & { ...; }, "src" | ... 259 more ... | "useMap"> & Partial<...>, "src" | ... 260 more ... | "useMap"> & { ...;...'.

I can overcome that by casting const Image = ... `` as any

But I don't like that any. Maybe it's an easy answer for someone familiar with styled components code...

Upvotes: 1

Views: 3665

Answers (1)

Jacob Gillespie
Jacob Gillespie

Reputation: 4081

You will want to add the IImageProps to the final tagged template call, to signal that those are the custom props that your Styled Component adds in addition to the <img> props:

const Image = styled.img.attrs((props: IImageProps) => ({
  style: { transform: `translate(0, -${props.frame * props.width}px)` },
}))<IImageProps>``

Note that you can also move the type annotation from the (props: IImageProps) to the .attrs type parameter:

const Image = styled.img.attrs<IImageProps>(props => ({
  style: { transform: `translate(0, -${props.frame * props.width}px)` },
}))<IImageProps>``

That way, props will be your custom interface plus all the built-in props for img.

Upvotes: 5

Related Questions