Search code examples
reactjstypescriptstyled-components

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...


Solution

  • 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.