Emotion: styled types do not support object notation

Created on 28 Jun 2019  路  9Comments  路  Source: emotion-js/emotion

Current behavior:

styled types do not support object notation.

According to the documentation (https://emotion.sh/docs/typescript#passing-props), it should be possible to write

interface ImageProps {
  src: string;
  width: number;
};

const Image1 = styled('div')(
  {
    backgroundSize: 'contain',
  },
  (props: ImageProps) => ({
    width: props.width,
    background: `url(${props.src}) center center`,
  })
);

but it give me that error locally

Argument of type '{ backgroundSize: string; }' is not assignable to parameter of type 'TemplateStringsArray'.
Object literal may only specify known properties, and 'backgroundSize' does not exist in type 'TemplateStringsArray'.ts(2345)

Looking at the types test (https://github.com/emotion-js/emotion/blob/master/packages/styled/types/tests.tsx) it only seems that string literals are supported.

Where types for object notation supported? Is it a bug?

Environment information:

"@emotion/core": "10.0.14"
"@emotion/is-prop-valid": "0.8.2"
"@emotion/styled": "10.0.14"
"react": "^16.8.6"
bug needs triage

Most helpful comment

The preferred way to pass a props type to styled is:

const Image = styled("div")<ImageProps>(
  {
    backgroundSize: "contain",
    zIndex: 30
  },
  (props) => ({
    width: props.width,
    background: `url(${props.src}) center center`
  })
);

It's the most "correct one" - because it's a central place where you can put it and thus have it available in all interpolations automatically.

Also when dealing with overloads TS always tries to match top-down and if it doesn't find a match then it reports the error about the last unmatched overload. I suspect that it couldn't match the first one for you because you have annotated props inside your interpolation, but that typing was too restrictive (? not sure about exact reason though) and it didn't match against "expected" Props & { theme: any }. It then has tried to match against the second one but that one failed quickly because the first argument was not a template.

All 9 comments

Which tsconfig.json are you using? I can always reproduce it

We have noticed a similar issue since updating to "@emotion/core": "10.0.14" and "@emotion/styled": "10.0.14".

We are passing around the theme props to our styled components. Up until 10.0.14 this has worked fine with components as the one below. But since 10.0.14, we started to get errors like

error TS2741: Property 'theme' is missing in type '{ children: Element[]; }' but required in type '{ theme: typeof import("/Users/[removed-for-privacy-reasons]/src/themes/[removed-for-privacy-reasons]"); }'.

190                 <Range>
                     ~~~~~

  node_modules/@emotion/styled-base/types/index.d.ts:33:11
    33   : P & { theme: T }
                 ~~~~~
    'theme' is declared here.

Examples

// Worked pre 10.0.14, but blows up in 10.0.14
const Range = styled.div(({ theme }) => ({
    display: 'flex',
    justifyContent: 'space-between',
    font: theme.fonts.desktop.medium,

    [theme.breakpoints.mobileAndLower]: {
        font: theme.fonts.mobile.medium,
    },
}));
// Works with all versions
const Range = styled.div<{}>(({ theme }) => ({
    display: 'flex',
    justifyContent: 'space-between',
    font: theme.fonts.desktop.medium,

    [theme.breakpoints.mobileAndLower]: {
        font: theme.fonts.mobile.medium,
    },
}));

Conclusion

It seems that the theme prop will never be added if we don't provide a type to the styled component. For now it works if we add <{}> to all styled components that takes theme as a prop. But it feels like we shouldn't have to.

If I use create-react-app locally I can reproduce the error, but I can't in CodeSandBox. I'll continue to investigate

Which tsconfig.json are you using? I can always reproduce it

Hard to tell exactly - codesandbox uses CRA template, so it should be some shape of this. Please provide a repository with the issue reproduced so we can reliably investigate this.

@eriktoyra

Can't reproduce your issue on codesandbox. Please provide a repository with the issue reproduced so we can reliably investigate this 馃槈

@Andarist Here's a repo that reproduces the issue https://github.com/thomasthiebaud/emotionstyled-tsissue

The preferred way to pass a props type to styled is:

const Image = styled("div")<ImageProps>(
  {
    backgroundSize: "contain",
    zIndex: 30
  },
  (props) => ({
    width: props.width,
    background: `url(${props.src}) center center`
  })
);

It's the most "correct one" - because it's a central place where you can put it and thus have it available in all interpolations automatically.

Also when dealing with overloads TS always tries to match top-down and if it doesn't find a match then it reports the error about the last unmatched overload. I suspect that it couldn't match the first one for you because you have annotated props inside your interpolation, but that typing was too restrictive (? not sure about exact reason though) and it didn't match against "expected" Props & { theme: any }. It then has tried to match against the second one but that one failed quickly because the first argument was not a template.

Thanks for the help. I'll close this issue for now.

My code was a copy paste from the documentation, so it probably will be better to remove it from there if it is not correct (https://emotion.sh/docs/typescript#passing-props)

My code was a copy paste from the documentation, so it probably will be better to remove it from there if it is not correct (https://emotion.sh/docs/typescript#passing-props)

Oh, I haven't realized that. Maybe @Ailrun could take a look at this one to see why the first overload is not being able to be matched here, I wonder if it has changed with any particular version of TS or was it always the case 馃

Was this page helpful?
0 / 5 - 0 ratings