score:0

I'm not sure it makes sense to use styled-components or the material styled HOC because those are better for setting fixed styles which don't depend on Props.

But you can set the styles that you want on through the style prop of the Button component.

Here's a solution that seems to work:

interface AddedProps {
  theme: Theme;
  color: string;
};

type InnerProps = AddedProps & Omit<ButtonProps, keyof AddedProps>

const InnerProfile = ({color, theme, ...other}: InnerProps) => (
  <Button
    {...other}
    style={{
      ...other.style,
      paddingTop: 20,
      paddingBottom: 20,
      backgroundColor: color,
      color: (theme as MyTheme).sidebar?.profile?.backgroundColor,
    }}
  />
)

export const Profile = withTheme( InnerProfile );

We say that our Profile needs two AddedProps: the color and the theme. We write InnerProfile assuming that those props exist. Then in a separate step we apply the withTheme HOC so that the theme prop will be set. The color prop is required and must be set by you when using <Profile />.

Now for this wonky craziness:

color: (theme as MyTheme).sidebar?.profile?.backgroundColor

What's happening is that withTheme knows that it injects a theme but it doesn't know that it injects your specific theme so typescript doesn't know that the theme has a property sidebar at all, since that's a custom property that you added. You need to tell typescript "assume this theme has the properties of MyTheme" by doing (theme as MyTheme). You can get that type MyTheme by using typeof on the object where you created your custom theme.

Since I don't have access to your custom theme object, what I did was define an interface that has the needed property path, but with everything optional. That looks like:

interface MyTheme extends Theme {
  sidebar?: {
    profile?: {
      backgroundColor?: string;
    } 
  }
}

And I used typescript ?. notation when accessing sidebar?.profile?.backgroundColor to avoid the "object is possibly undefined" error.

score:1

For those who is using MUI5 and wondering how this could be done you can find an example in this github comment from material ui.

Copy of Typescript example:

const StyledComp = styled("div", {
  shouldForwardProp: (prop) => prop !== "color" && prop !== 'myProp',
})<{ myProp?: boolean; color?: string }>(({ theme, myProp, color }) => ({
  backgroundColor: myProp ? "aliceblue" : "red",
  color,
  padding: theme.spacing(1)
}));

<StyledComp myProp color="red" /> // typed safe

score:7

This problem has also almost drove me crazy, however I've managed to make it work with this approach:

import React from 'react'
import { styled, Theme, withTheme } from '@material-ui/core/styles'
import { Box, BoxProps } from '@material-ui/core'

interface SidebarSectionProps {
  padded?: boolean
  theme: Theme
}

export const SidebarSectionComponent = styled(
  (props: SidebarSectionProps & BoxProps) => <Box {...props} />
)({
  marginBottom: ({ theme }: SidebarSectionProps) => theme.spacing(3),
  paddingLeft: ({ theme, padded }: SidebarSectionProps) =>
    padded ? theme.spacing(3) : 0,
  paddingRight: ({ theme, padded }: SidebarSectionProps) =>
    padded ? theme.spacing(3) : 0
})

export const SidebarSection = withTheme(SidebarSectionComponent)

Related Query

More Query from same tag