Emotion: Help with Typescript and withTheme (emotion-theming)

Created on 11 Jan 2019  路  3Comments  路  Source: emotion-js/emotion

  • emotion version: 9.2.12
  • react version: 16.5.2
  • emotion-theming: 9.2.9
  • typescript: 3.2.2

Relevant code:

import React from 'react';
import PropTypes from 'prop-types';

import { css, cx } from 'emotion';
import { withTheme } from 'emotion-theming';

interface Props {
  size?: 'small' | 'medium' | 'large';
  imageSrc: string;
  alt: string;
  customClass?: string;
}

const Icon: React.FunctionComponent<Props> = (props) => {
  const { size, imageSrc, alt, customClass, theme } = props;

  const baseClass = css`
    cursor: pointer;
    display: block;
    border-radius: initial;
  `;

  const sizeClass = ((sizeString) => {
    const medium = css`
      width: calc(${theme.spacing.small} * 1.5);
      height: calc(${theme.spacing.small} * 1.5);
    `;

    switch (sizeString) {
      case 'small':
        return css`
          width: ${theme.spacing.small};
          height: ${theme.spacing.small};
          user-select: none;
        `;
      case 'medium':
        return medium;
      case 'large':
        return css`
          width: ${theme.spacing.medium};
          height: ${theme.spacing.medium};
        `;
      default:
        return medium;
    }
  })(size);

  const className = cx(baseClass, sizeClass, customClass);

  return (
    <img src={imageSrc} alt={alt} className={className} draggable={false} />
  );
};

Icon.defaultProps = {
  size: 'large',
  customClass: undefined,
};

export const BaseIcon = withTheme(Icon);

What you did:
I'm trying to upgrade my existing code to typescript (I'm new to typescript) but I keep getting errors when props are not set to any. I need to pass my own custom props to the component but the compiler complains that theme does not have a type in my interface.

It works fine if I set props to any but from what I gather that really defeats the purpose of using typescript.

I've looked around existing issues, for example: https://github.com/emotion-js/emotion/issues/802#issuecomment-413062130 but I can't really wrap my head around this bit here:

However, to use custom props, you should pass generic parameter instead of annotate type to function argument (since type annotation will hide the props.theme because your Props type does not explicitly have theme in it).

I'm sure this is more of a typescript problem than an emotion problem but I thought you guys might better understand what I'm trying to do.

Could someone help me figuring out how I can pass my custom props without settings props to any?

What happened:
The error I get is: Property 'theme' does not exist on type 'Props & { children?: ReactNode; }

Also, thanks in advance! :-)

Most helpful comment

Hello, I took liberty to refactor your code to show you the way I like to do things with emotion & typescript. I'm by no means expert or smth - so just take what you want :smile:

CODESANDBOX: https://codesandbox.io/s/xol4jjv1jq

However, to use custom props, you should pass generic parameter instead of annotate type to function argument (since type annotation will hide the props.theme because your Props type does not explicitly have theme in it).

I.e. when you use HOC (withTheme) you need to provide type for Injected Props. In those cases you provide some new props to wrapper component that styled do not about and can't infer. So you did it by something like this styled<Props> and then you would loose theme type from styled because you essentially retyped component. The only important thing is that this comment is history now because Ailrun improved typings and now all this works like magic :P There's example in my codesandbox.

Your direct question:
Prop theme does not exist because it's injected prop. You should type your component like this React.Component<Props & ThemeProps>

Imo few general things that I would advice you:
1) Use themed styled interface (https://emotion.sh/docs/typescript#define-a-theme)
2) Provide generic value (injected props) when you have prop that belong to Wrapper component BUT NOT to underlying component. Otherwise all automatic.
3) Do no use cx => use dynamic composition and css prop (// personal preference!)
4) Keep styles outside component
5) Use styled() instead withTheme() when you're just styling

You can check this to understand HOCs props more: https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb

I hope it help you some :upside_down_face:

All 3 comments

Hello, I took liberty to refactor your code to show you the way I like to do things with emotion & typescript. I'm by no means expert or smth - so just take what you want :smile:

CODESANDBOX: https://codesandbox.io/s/xol4jjv1jq

However, to use custom props, you should pass generic parameter instead of annotate type to function argument (since type annotation will hide the props.theme because your Props type does not explicitly have theme in it).

I.e. when you use HOC (withTheme) you need to provide type for Injected Props. In those cases you provide some new props to wrapper component that styled do not about and can't infer. So you did it by something like this styled<Props> and then you would loose theme type from styled because you essentially retyped component. The only important thing is that this comment is history now because Ailrun improved typings and now all this works like magic :P There's example in my codesandbox.

Your direct question:
Prop theme does not exist because it's injected prop. You should type your component like this React.Component<Props & ThemeProps>

Imo few general things that I would advice you:
1) Use themed styled interface (https://emotion.sh/docs/typescript#define-a-theme)
2) Provide generic value (injected props) when you have prop that belong to Wrapper component BUT NOT to underlying component. Otherwise all automatic.
3) Do no use cx => use dynamic composition and css prop (// personal preference!)
4) Keep styles outside component
5) Use styled() instead withTheme() when you're just styling

You can check this to understand HOCs props more: https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb

I hope it help you some :upside_down_face:

Wow! Thank you so much for this well written response! How you explained it made it all click for me :)

I'll take your advice and switch over to styled instead of withTheme and do some more reading up on typescript and HOCs.

Thanks again! 馃槃

Best regards, Mikael

Happy to help :+1:

Really btw. I wrote in some comment that SFC > FunctionalComponent, well, - I just checked react 16.8 and saw they depreciated it. FunctionalComponent is a proper way.

Was this page helpful?
0 / 5 - 0 ratings