styled-components: Typescript error with nested styled, themed components

Version

1.0.5

Reproduction

Not sure how to replicate this in webpackbin

Steps to reproduce

Create a theme file, as per the typescript guidance here

import * as styledComponents from 'styled-components';
import { ThemedStyledComponentsModule } from 'styled-components';

interface MyTheme {
  backgroundColor: string;
}

const defaultTheme: MyTheme = {
  backgroundColor: blue;
};

const {
  default: styled,
  css,
  injectGlobal,
  keyframes,
  ThemeProvider,
} = styledComponents as ThemedStyledComponentsModule<MyTheme>;

export default styled;
export { css, injectGlobal, keyframes, ThemeProvider, MyTheme, defaultTheme };

Create two styled components.

  • The inner component wraps an input element.
  • The outer component wraps the inner component.
  • Expose the input elements API to the outer component using the {...others} = props pattern.
import * as React from 'react';
import styled, { defaultTheme } from './theme';

// Inner Component
interface IInnerComponentProps extends React.HTMLProps<HTMLInputElement> {
  foo?: string;
}

const InnerComponent: React.StatelessComponent<IInnerComponentProps> = props => {
  const {foo, ...others} = props;
  return <input type="text" {...others}/>;
};

const InnerStyled = styled(InnerComponent)`
  background: red;
`;

InnerStyled.defaultProps = defaultTheme;

// Outer component
interface IMyComponentProps extends React.HTMLProps<HTMLElement> {
  bar?: string;
}

const MyComponent: React.StatelessComponent<IMyComponentProps> = props => {
  const {bar, ...others} = props;
  return <div><InnerStyled type="text" {...others}/></div>;
};

const MyComponentStyled = styled(MyComponent)`
  color: green;
`;

MyComponentStyled.defaultProps = {
  theme: defaultTheme,
};

Expected Behavior

the line return <div><InnerStyled type="text" {...others}/></div>; should not generate a typescript error.

Actual Behavior

The following typescript error is generated

Type '{ value: string; ghostValue?: string; theme?: any; defaultChecked?: boolean; defaultValue?: strin...' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<ThemedOuterStyledProps<IGhostedTextInput...'.
  Type '{ value: string; ghostValue?: string; theme?: any; defaultChecked?: boolean; defaultValue?: strin...' is not assignable to type 'IntrinsicClassAttributes<Component<ThemedOuterStyledProps<IGhostedTextInputProps, IGhostedTextInp...'.
    Types of property 'ref' are incompatible.
      Type 'Ref<HTMLInputElement>' is not assignable to type 'Ref<Component<ThemedOuterStyledProps<IGhostedTextInputProps, IGhostedTextInputTheme>, ComponentSt...'.
        Type '(instance: HTMLInputElement) => any' is not assignable to type 'Ref<Component<ThemedOuterStyledProps<IGhostedTextInputProps, IGhostedTextInputTheme>, ComponentSt...'.
          Type '(instance: HTMLInputElement) => any' is not assignable to type '(instance: Component<ThemedOuterStyledProps<IGhostedTextInputProps, IGhostedTextInputTheme>, Comp...'.
            Types of parameters 'instance' and 'instance' are incompatible.
              Type 'Component<ThemedOuterStyledProps<IGhostedTextInputProps, IGhostedTextInputTheme>, ComponentState>' is not assignable to type 'HTMLInputElement'.
                Property 'accept' is missing in type 'Component<ThemedOuterStyledProps<IGhostedTextInputProps, IGhostedTextInputTheme>, ComponentState>'. 

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 7
  • Comments: 22 (8 by maintainers)

Most upvoted comments

Closing since the original issue was answered by @Igorbek!

It’s not a bug 😃 you’re using the default HTMLProps and trying to pass a ref, but unfortunately the ref is now pointing to an instance of the styled component and not the underlying element ref.

You’ll either want to add a new type for ref, or more likely use innerRef instead

cc @styled-components/typers

@gargantuan this should work:


function toInnerRef<T>(ref: undefined | React.Ref<T>): undefined | ((instance: T | null) => void) {
    if (!ref) {
        return undefined;
    }
    if (typeof ref === 'string') {
        throw new Error('name refs are not supported.');
    }
    return ref;
}

const MyComponent: React.StatelessComponent<IMyComponentProps> = props => {
    const { bar, ref, ...others } = props;
    const innerRef = toInnerRef(ref);
    return <div><InnerStyled type="text" {...others} innerRef={innerRef} /></div>;
};

Note, that ref from HTMLProps and ref from StyledComponent are not covariantly compatible. styled-components use innerRef to reach out the ref of its inner component.

Sorry to reopen an old issue, but still running into issues with this

@Igorbek 's solution didn’t work for me https://github.com/styled-components/styled-components/issues/824#issuecomment-315815349

If ellipsis defined as (width?: string | number | undefined) => Object then it indeed incorrect argument. An interpolation function accepts an object that consists of props and theme. It can accept neither width nor number.

The fix should be something like this:

const Heading = styled.div`
  ${(props: { width: number; }) => ellipsis(props.width)};
`;

<Header width={12} />; // usage
  • The interpolation now is a proper function taking props
  • The type of extra prop (width) was explicitly provided

Even more simple example generates compile-time error in TypeScript:

const LogoBlock = styled.div`
  background-color: lime;`;

const HeaderBlock = styled.div`
  border: 1px solid #333;

  > ${LogoBlock} {
    background: red;
  }
`;

It gave me following error:

error TS2345: Argument of type 'ComponentClass<ThemedOuterStyledProps<HTMLProps<HTMLDivElement>, any>>' is not assignable to parameter of type 'Interpolation<ThemedStyledProps<HTMLProps<HTMLDivElement>, any>>'.
  Type 'ComponentClass<ThemedOuterStyledProps<HTMLProps<HTMLDivElement>, any>>' is not assignable to type 'ReadonlyArray<string | number | InterpolationFunction<ThemedStyledProps<HTMLProps<HTMLDivElement>...'.
    Property 'find' is missing in type 'ComponentClass<ThemedOuterStyledProps<HTMLProps<HTMLDivElement>, any>>'.

Any ideas how to handle this?

I’m still running into this issue. JSX element type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)>) | (new (props: any) => Component<any, any, any>)>' is not a constructor function for JSX elements.

Yep, there’s still no fixed.

But, I have fixed my issue by creating new React.Component

const ButtonDefault = styled(Button)`
  background-color: ${ (props) => props.intent ? props.intent : Color.blue } !important;
`

export default class extends React.Component<React.HTMLAttributes<{}> & typeof ButtonDefault.propTypes, {}> {
  public render () {
    return <ButtonDefault {...this.props}/>
  }
}

@gargantuan sorry, I misread his comment 😃

It’s been addressed in #837 and #882. However I’m still having problems when I updated styled-components to v2.0.1. I’m creating an issue if I’m not able to solve them.

class Nav extends React.Component<any, IProps> {
                                  ^^^ here should be props