Search code examples
reactjstypescriptmaterial-uistyled-components

Typescript doesn't recognize common object for styles in MUI styled component


I have some different styled component with some common styles, so I've created a function that takes in theme and returns the common styles. It looks something like this:

const getCommonStyled = (theme: Theme) => ({
  height: '100%',
  display: 'flex',
  flexDirection: 'column',
  padding: theme.spacing(2),
})

const StyledComponent1 = styled('div')(({ theme }) => ({
  ...getCommonStyled(theme),
  color: 'blue'
}))

const StyledComponent2 = styled(Typography)(({ theme }) => ({
  ...getCommonStyled(theme),
  backgroundColor: 'blue'
}))

const StyledComponent3 = styled('span')(({ theme }) => ({
  ...getCommonStyled(theme),
  width: 100,
  height: 100,
}))

This seems simple enough, but Typescript will not allow it and its error message is confusing:

error TS2769: No overload matches this call. The last overload gave the following error. Argument of type '({ theme }: MUIStyledCommonProps & ClassAttributes & HTMLAttributes & { ...; }) => { ...; }' is not assignable to parameter of type 'TemplateStringsArray'.

How can I type getCommonStyled so that these errors go away?


Solution

  • When you don't explicitly specify the return type for getCommonStyled, TypeScript is inferring the following:

    const getCommonStyled: (theme: Theme) => {
         height: string;
         display: string;
         flexDirection: string;
         padding: string; 
    }
    

    This works fine for any CSS properties where the TypeScript definition allows string values, but the definition for flexDirection is more specific:

    From https://github.com/frenic/csstype/blob/v3.1.2/index.d.ts#L18882:

    export type FlexDirection = Globals | "column" | "column-reverse" | "row" | "row-reverse";
    

    The way I would recommend fixing this is to specify a return type of React.CSSProperties:

    const getCommonStyled = (theme: Theme): React.CSSProperties => ({
      height: '100%',
      display: 'flex',
      flexDirection: 'column',
      padding: theme.spacing(2),
    })
    

    React.CSSProperties leverages the csstype package as does MUI's types for the styled API so MUI will recognize a spread of that type as compatible. Specifying that return type will also do more immediate type checking of that method (e.g. you'll get a TypeScript error directly in getCommonStyled if you set flexDirection to something other than one of the allowed values).