Definitelytyped: [@types/styled-components] Cannot extend a component with static getDerivedStateFromProps

Created on 21 Feb 2019  路  2Comments  路  Source: DefinitelyTyped/DefinitelyTyped

  • [x] I tried using the @types/xxxx package and had problems.
  • [x] I tried using the latest (3.3.3) stable version of tsc. https://www.npmjs.com/package/typescript
  • [x] I have a question that is inappropriate for StackOverflow. (Please ask any appropriate questions there).
  • [x] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) the authors (see Definitions by: in index.d.ts) so they can respond.

    • Authors: @Igorbek, @Igmat, @Lavoaster, @Jessidhia, @JKillian

Extending from a component with a static getDerivedStateFromProps hook throws a compilation error. This component:

export class Foo extends React.Component<FooProps, {}> {
    public static getDerivedStateFromProps = (props: FooProps, state: {}) => {
        return {};
    }

    public render = () => (<div>Foo</div>);
}

const StyledFoo = styled(Foo)``;

raises this error:

2345[QF available]: Argument of type 'typeof Foo' is not assignable to parameter of type '"symbol" | "object" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | ... 154 more ... | FunctionComponent<...>'. Type 'typeof Foo' is not assignable to type 'ComponentClass<any, any>'. Types of property 'getDerivedStateFromProps' are incompatible. Type '(props: FooProps, state: {}) => {}' is not assignable to type 'GetDerivedStateFromProps<any, any>'. Types of parameters 'props' and 'nextProps' are incompatible. Property 'x' is missing in type 'Readonly<any>' but required in type 'FooProps'.

If I remove the static keyword, the error goes away though.

Most helpful comment

@fvictorio the problem is that your return type is implicit and therefore results in being wider than allowed.

Consider this type declaration from @types/react:

type GetDerivedStateFromProps<P, S> =
    /**
      * Returns an update to a component's state based on its new props and old state.
      *
      * Note: its presence prevents any of the deprecated lifecycle methods from being invoked
      */
    (nextProps: Readonly<P>, prevState: S) => Partial<S> | null; // <---------------------

Note how the function returns Partial<S>, which means all is optional. With an implicit return type, the compiler doesn't know that all is optional.

Try typing your function like so:

import { GetDerivedStateFromProps } from "react"

export class Foo extends React.Component<FooProps, {}> {
    public static getDerivedStateFromProps: GetDerivedStateFromProps<FooProps, {}> = (props, state) => {
        return {};
    }

    public render = () => (<div>Foo</div>);
}

const StyledFoo = styled(Foo)``; // works fine

All 2 comments

@fvictorio the problem is that your return type is implicit and therefore results in being wider than allowed.

Consider this type declaration from @types/react:

type GetDerivedStateFromProps<P, S> =
    /**
      * Returns an update to a component's state based on its new props and old state.
      *
      * Note: its presence prevents any of the deprecated lifecycle methods from being invoked
      */
    (nextProps: Readonly<P>, prevState: S) => Partial<S> | null; // <---------------------

Note how the function returns Partial<S>, which means all is optional. With an implicit return type, the compiler doesn't know that all is optional.

Try typing your function like so:

import { GetDerivedStateFromProps } from "react"

export class Foo extends React.Component<FooProps, {}> {
    public static getDerivedStateFromProps: GetDerivedStateFromProps<FooProps, {}> = (props, state) => {
        return {};
    }

    public render = () => (<div>Foo</div>);
}

const StyledFoo = styled(Foo)``; // works fine

Oh, I see. I will assume that there's nothing to do about this then and close the issue. Thanks!

Was this page helpful?
0 / 5 - 0 ratings