TypeScript issue with styled-component's "css" prop and Storybook
Asked Answered
B

3

22

I'm having issues with enabling styled-component's css prop in TypeScript as soon as I import something from Storybook, because Storybook depends on @emotion/core, whose type declaration defines Emotion's own css prop. The error stems from the fact that the two css prop types being incompatible.

I'm familiar with the recommended way of enabling the css prop in TypeScript (including the answer on Stack Overflow), as well as suggestions on how to handle the Storybook problem explained above.

This is the application code, index.tsx:

import React from 'react'
import { css } from 'styled-components'

export default function App() {
  return (
    <h1
      css={css`
        color: red;
      `}
    >
      Hello world!
    </h1>
  )
}

and this is the type declaration, styled-components.d.ts, which contains the attempted fix:

import { CSSProp } from "styled-components";

// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245#issuecomment-780019806

declare module 'react' {
  interface DOMAttributes<T> {
    css?: CSSProp
  }
}
declare global {
  namespace JSX {
    interface IntrinsicAttributes {
      css?: CSSProp
    }
  }
}

This works until I import something in index.tsx from Storybook which imports @emotion/core, for example:

import { storiesOf } from '@storybook/react'
// ...

Then TypeScript fails with the following errors:

index.tsx:8:7 - error TS2322: Type 'FlattenSimpleInterpolation' is not assignable to type 'InterpolationWithTheme<any>'.
  Type 'readonly SimpleInterpolation[]' is not assignable to type 'ObjectInterpolation<undefined>'.
    Types of property 'filter' are incompatible.
      Type '{ <S extends SimpleInterpolation>(predicate: (value: SimpleInterpolation, index: number, array: readonly SimpleInterpolation[]) => value is S, thisArg?: any): S[]; (predicate: (value: SimpleInterpolation, index: number, array: readonly SimpleInterpolation[]) => unknown, thisArg?: any): SimpleInterpolation[]; }' is not assignable to type 'string | string[] | undefined'.
        Type '{ <S extends SimpleInterpolation>(predicate: (value: SimpleInterpolation, index: number, array: readonly SimpleInterpolation[]) => value is S, thisArg?: any): S[]; (predicate: (value: SimpleInterpolation, index: number, array: readonly SimpleInterpolation[]) => unknown, thisArg?: any): SimpleInterpolation[]; }' is missing the following properties from type 'string[]': pop, push, concat, join, and 27 more.

8       css={css`
        ~~~

  node_modules/@emotion/core/types/index.d.ts:84:5
    84     css?: InterpolationWithTheme<any>
           ~~~
    The expected type comes from property 'css' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>'

styled-components.d.ts:7:5 - error TS2717: Subsequent property declarations must have the same type.  Property 'css' must be of type 'InterpolationWithTheme<any>', but here has type 'CSSProp<any> | undefined'.

7     css?: CSSProp
      ~~~

  node_modules/@emotion/core/types/index.d.ts:84:5
    84     css?: InterpolationWithTheme<any>
           ~~~
    'css' was also declared here.

styled-components.d.ts:13:7 - error TS2717: Subsequent property declarations must have the same type.  Property 'css' must be of type 'InterpolationWithTheme<any>', but here has type 'CSSProp<any> | undefined'.

13       css?: CSSProp
         ~~~

  node_modules/@emotion/core/types/index.d.ts:96:7
    96       css?: InterpolationWithTheme<any>
             ~~~
    'css' was also declared here.

Here's the CodeSandbox created from this repository which reproduces this issue.

Bildungsroman answered 1/3, 2021 at 13:25 Comment(2)
I suggest cloning your reproducible example over to stackblitz – Chloroplast
@MikeS. here's the CodeSandbox as I'm not familiar with Stackblitz, I hope that helps πŸ™‚ – Bildungsroman
F
0

According to the documentation for the styled-components library, it seems that you should be importing style rather than css.

I created an example showing how to do this:
https://codesandbox.io/s/react-typescript-forked-1xkww4?file=/src/App.tsx

Alternatively, you could try casting css as something else, although I'm not sure it would help with the type definition conflict you're having.

import { css as StyledCss } from 'styled-components';

Hopefully this helps.

Firedog answered 17/5, 2022 at 13:35 Comment(3)
What do you mean by style? Did you mean styled? Btw, based on the documentation it looks like the css is no longer needed at all for the css prop, not sure when that happened: styled-components.com/docs/api#css-prop But type checking works if I pass a regular string instead of using the css import. – Bildungsroman
If you would care to find out what happened there, in which version this changed and why, and post this in a new answer, I'd mark it as correct. I'm not using styled-components anymore so I have low motivation. – Bildungsroman
It looks like you are not using styled-components properly. See this: styled-components.com – Winkelman
C
0

Personally, I'm not really familiar with that module. The way you're going with this, to me, seems like a very complex approach. Whenever I build a re-usable component, I always give variation through the props, like so:

enum ButtonVariant {
  PRIMARY = 'primary',
  SECONDARY = 'secondary',
  SUBMIT = 'submit',
  // etc ...
}

interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  children: JSX.Element | string;
  // etc ...
}

const Button = (props: ButtonProps): JSX.Element => {
  const {
    variant = ButtonVariant.PRIMARY,
    className = '',
    children,
    ...restProps
  } = props;

  const primaryClassNames = 'some-classes';
  const secondaryClassnames = 'some-classes';

  // functions, handlers, etc ...

  return (
    <>
      {
        switch(variant) {
          case ButtonVariant.PRIMARY:
            return (
              <button
                className={`${primaryClassNames} ${className}`} {...restProps}
              >
                {children}
              </button>
            );
          case ButtonVariant.SECONDARY:
            return (
              <button
                className={`${secondaryClassNames} ${className}`} {...restProps}
              >
                {children}
              </button>
            );
         // etc ...
        }
      }
    </>
  );
}

export default Button

I hope this helps your case.

Crescen answered 4/6 at 7:27 Comment(0)
E
-1
import React from 'react'
import { Css } from 'styled-components'

export default function App() {
  return (
    
    
    <Css
     color={"red"}
      className={`${classes}`}
      {...props}
    >
 
     <h1>
      Hello world!
    </h1>
    </Css>
    
  )
}
Eolithic answered 3/5, 2023 at 8:51 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center. – Ballard

© 2022 - 2024 β€” McMap. All rights reserved.