Definitelytyped: Problem with connect() in @types/react-redux introduced in 4.4.41

Created on 6 Jun 2017  ·  44Comments  ·  Source: DefinitelyTyped/DefinitelyTyped

Using @types/react-redux version 4.4.40, the following code

import * as React from 'react';
import {connect} from "react-redux";

interface State {
    y: number;
}

// Own properties
interface OP {
    x: number;
}

// State properties
interface SP {
    y: number;
}

function mapStateToProps(state: State, ownProps: OP): OP & SP {
    return {
        x: ownProps.x,
        y: state.y,
    };
}

class TestComp extends React.Component<OP & SP, null> {
    render() {
        return <p>Hello</p>;
    }
}

export default connect(mapStateToProps)(TestComp);

would let me use TestComp as follows:

const tc = <TestComp x={42}/>;

With version 4.4.41 and 4.4.42 the above leads to the following compilation error:

error TS2324: Property 'y' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<OP & SP, ComponentState>> & { children?:...'.

/cc @thasner @seansfkelley @tkqubo @blakeembrey @sandersn @mhegazy @andy-ms @hans-permana

Most helpful comment

Breaks again in Typescript 3.0.1. Hi Thien!

All 44 comments

I'm getting this error as well. It feels like one of the typedefs accidentally swapped TOwnProps and TMergedProps/TStateProps, but I only took a cursory look at index.d.ts.

Any update on this issue? I think it can be a showstopper for new developer coming to redux + typescript, thanks for you great work.

@brauliodiez I merged @blakeembrey's fix #16969. It should be available in an hour or so. Let me know if it helps.

I think we're still seeing this issue even with the fixes from https://github.com/DefinitelyTyped/DefinitelyTyped/pull/16969. Here's a dummy component I made to try and isolate the issue:

import * as React from "react"
import { connect, MapStateToProps, MapDispatchToPropsFunction } from "react-redux"


interface RootState {
  globalA: string
  globalB: number
}

interface DumbComponentProps {
  a: string
  b: number
  c?: number
}

const DumbComponent = (props: DumbComponentProps): JSX.Element => {
  return (
    <div>Something: {props.a} {props.b} {props.c}</div>
  )
}

type ConnectedStateProps = Pick<DumbComponentProps, "a" | "b">
type ConnectedOwnProps = Pick<DumbComponentProps, "c">

const mapStateToProps: MapStateToProps<ConnectedStateProps, ConnectedOwnProps> = (
    state: RootState): ConnectedStateProps => {
  return {
    a: state.globalA,
    b: state.globalB
  }
}

const SmartComponent = connect(
  mapStateToProps
)(DumbComponent)

export default SmartComponent

I then use SmartComponent somewhere and pass in c (note that a & b are supposed to come from the state).

...
return (
  <SmartComponent c{2} />
)

The error I get is:

error TS2322: Type '{ c: 2; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<DumbComponentProps, ComponentState>> & R...'.
  Type '{ c: 2; }' is not assignable to type 'Readonly<DumbComponentProps>'.
    Property 'a' is missing in type '{ c: 2; }'.

Inspecting the types in VS Code, I notice that it's picking up the wrong type for SmartComponent. It thinks it's React.ComponentClass<DumbComponentProps> ... but it should be React.ComponentClass<Pick<DumbComponentProps, "c">> (i.e, it should only accept OwnProps. This makes sense from the types, where ComponentDecorator is a function that returns a ComponentClass<T> (where T extends TOwnProps) ... so unfortunately I don't know why it's not mapping correctly.

Am I doing something obviously wrong here? Is there a way to get the compiler to show me step by step what it's inferring?

It looks like mapDistpachToProps is not optional now. Is it ok or is a bug?

An update to my comment above, after digging around a little - I've created a test case that currently fails (but I believe shouldn't) and a fix to the typings that handles my case, but doesn't handle the case where it has to guess what the OwnProps are (e.g connect()(DumbComponent)). My changeset is here:
https://github.com/DefinitelyTyped/DefinitelyTyped/compare/master...ritwik:react-redux/fix-ownprop-inference
Would like to help with solving this but I'm hitting the limits of my knowledge with typescript. If anyone has thoughts on my test case or suggestions, would love to discuss those.

Just closing the loop here - https://github.com/DefinitelyTyped/DefinitelyTyped/pull/17196 looks like it fixes this for our codebase.

Was this solved? Im currently getting this problem in ^5.0.8 (IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick ...). The only difference is that I am using types instead of interfaces.

anyone? this is a real showstoppper for me unfortunately...

@proProbe Could you provide your @types/react-redux, @types/react, and redux versions, and a sample that reproduces the error?
Also CC package authors: @tkqubo @thasner @kenzierocks @clayne11 @tansongyang @nicholasboll

Sorry for the late answer! I was working with a sample to reproduce it. I then suddenly realized that this has been fixed in @types/react-redux: "^5.0.8". Thanks though!

Still getting it with "^5.0.10" :(

"@types/react": "^15.6.4",
"redux": "3.7.2",
"@types/react-redux": "^5.0.10"

I can share a code snippet with a package author but unfortunately not publicly.

Downgrading to 4.4.40 as suggested above immediately removed the error.

I was able to resolve this in my own code base by type-parameterizing connect on the type of the return value of mapStateToProps and what I wanted ownProps to be. Taken out of context if I want to call my product list with a list of product id's like:

<ProductList productIDs={productIDs} />

and have mapStateToProps to translate those id's into products to be shown in the list like:

const mapStateToProps = (state: RootState, ownProps: {productIDs: List<UUID>}) => {
  return { products: state.get('products').filter(
    (p: Product) => ownProps.productIDs.contains(p.uuid)) };
};

then I had to parameterize connect like:

const ProductList = connect<ProductListProps, void, {productIDs: List<UUID>}>(mapStateToProps)(ProductListComponent);

After some investigation. I found a simple solution. What you only have to do is to have "compilerOptions": { "strict": true } in your tsconfig.json if you're connecting a React.Component<,> component.

Typescript 2.8.x fixes this. Upgrade and give it a try.

Breaks again in Typescript 3.0.1. Hi Thien!

Breaking with TypeScript 3.2.2 too.

Does this issue has any updates?

Broke for me with Typescript 3.2.2 as well. However, annotating connect with connect<Props> seems to fix it.

I have same issue, even with annotating connect :/

I have the same issue too, no update at all?

Breaking with TypeScript 3.2.4 too.

It seems this will not get resolved soon.

To everyone having this issue, make sure you are importing your connected component properly. I switched an already-existing component to have a connect() function once it needed to access app state from redux. While doing this, I switched from exporting a stateless function to exporting my class component as default. When I didn't change how I was importing it in other files, this error popped up.

It is working.

Is there any workaround to at least silence this error locally?

Definitely seems like there is a flip flop somewhere in the OwnProps versus StateProps. I am seeing this issue where we have Redux connected component that has children.

All I did to silence this issue is to use <any>:

export default connect<any>(states, actions)(SomeComponent);

also works if you use compose:

export default compose<any>(
  connect(states, actions),
  withStyles(styles)
)(SomeFunctionalComponent);

This should silence the error when passing props to the 'smart'/connected component.

EDIT: I just re-read the thread again, and it seems like @themodernlife's workaround is the better workaround (ie. annotate connect or compose using Props interface or type):

Doesn't that also destroy all the type checking and Intellisense? So what is the point of adding the typings at all then? @dcefram

Yeah, it's a workaround just to silence the compiler and allow it to build to prevent this from being a "show stopper" (or in my case, to prevent me from re-writing everything away from TS or getting stalled until a fix is out). It's not THE solution though.

I too, am waiting for the fix for this :)

Better workaround is to manually type the connect call like this:

type StateProps = {
  // Attributes that you want mapped from redux store
}
type DispatchProps = {
  // Dispatch actions
}
type OwnProps = {
  // Other props that component expects when being created
}
type Props = StateProps & DispatchProps & OwnProps;
class ExampleComponent extends React.Component<Props> { /* ... */ }

const mapStateToProps = (state: State, ownProps: OwnProps): StateProps => { /* .. */ }
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { /* ... */ }

export default connect<StateProps, DispatchProps, OwnProps>
  (mapStateToProps, mapDispatchToProps)(ExampleComponent)

This should create correct connected component that expects OwnProps when being created and no other state nor dispatch props.

Negative. It still is trying to infer the types. Instead I have to override manually type a const and then return that as the default.

const connected: React.ComponentType<OwnProps> = connect<StateProps, DispatchProps, OwnProps>(......)(Component);
export default connected;

Facing the same issue with connect() using "@types/react-redux": "7.0.1" and "typescript": "3.4.5" . No solutions mentioned seem to helped. @TroySchmidt Can you share how you have used this "connected" in the parent component if possible some sample code would be very helpful

Hi, I had a similar error. react-redux doesn't seem to resolve the return value of mapStateToProps as "TInjectedProps", which causes the wrong exclusion in the Omit helper @types/react-redux index.d.ts:110 export type InferableComponentEnhancerWithProps.

the definition of the type in the connect function solves the problem

// connect Redux
export const RCHeader = connect
<IHeaderStateToProps, //returned by mapStateToProps
 {}, // not necessary
 IHeaderProps, // props you want to pass through jsx/tsx
 IHeaderConnectedReduxStateProps, // combined from IHeaderMapStateToProps and IHeaderProps
 {}// not necessary
>(
    (state: ISettingsState ): IHeaderStateToProps => {
        return {
            headerLogoURL: state.settings.headerLogoURL
        }
    },
    undefined,
    undefined,
    { forwardRef: true }
)(Header);
{
  "dependencies": {
    "react": "^16.8.6",
    "react-redux": "^6.0.1",
    "redux": "^4.0.1"
  },
  "devDependencies": {
    "@types/react": "^16.8.17",
    "@types/react-redux": "^7.0.6",
    "react-scripts-ts": "^4.0.8",
    "typescript": "^3.4.5"
  }
}

Is there any update for this?

Recently, I write a component which would connect to redux. When I use this component in other place, all the props of it are missing. And fix it by:

type StateProps = {
  /* some code */
};

type DispatchProps = {
  /* some code */
};

type OwnProps = {
  /* some code */
};

type Props = StateProps & DispatchProps & OwnProps;

const mapStateToProps = (state: IReduxState): StateProps => ({
  /* some code */
});

const mapDispatchToProps = (dispath: Dispatch<AnyAction>): DispatchProps => {
  /* some code */
};

const BaseComponent: React.FC<Props> = (props: Props) => {
  /* some code */
};

export const ConnectedComponent: React.ComponentType<OwnProps> = connect<
  StateProps,
  DispatchProps,
  OwnProps,
  IReduxState
>(
  mapStateToProps,
  mapDispatchToProps
)(BaseComponent);

dependencies are below:

{
  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^7.1.0",
    "redux": "^4.0.1",
  },
  "devDependencies": {
    "@types/react": "^16.8.22",
    "@types/react-dom": "^16.8.4",
    "@types/react-redux": "^7.1.0",
    "@types/redux": "^3.6.0",
    "typescript": "^3.5.2"
  }
}

adding all those types instead of having some basic type inference does not seem like a good solution to me...

This still seems like an issue. Fairly certain the problem was introduced in version 7.0.2, as I can install 7.0.1 and type inference works correctly.

Works for me.
@justemit posted correct approach that works on the last versions of react and redux.

@justemit Thank you this drove me crazy 🙌

Edit: I've fixed the issue using React.FC<Props> 😅

I think this issue is still alive and well in @types/react-redux@^7.1.2:

const propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.func.isRequired
};

// inferring the Props type
interface Props extends PropTypes.InferProps<typeof propTypes> {}
// although doing it explicitly also yields the same result
interface Props {
  foo: number,
  bar: (...args: any[]) => any
}

const Foo = (props : Props) => <></> // EDIT: incorrect way to define functional components
const Foo : React.FC<Props> = (props) => <></> // EDIT: the correct way
//          ^^^^^^^^

Foo.propTypes = propTypes;
/* Foo.defaultProps = {
  foo: 1,
  bar: () => {}
} //*/

const mapStateToProps = (state : any) => ({
  foo: 1
})
const mapDispatchToProps = {
  bar: () => {}
}
const Bar = connect(mapStateToProps, mapDispatchToProps)(Foo)

const Baz = () => <Bar /> // throws an error
Type '{}' is missing the following properties from type 'Readonly<Pick<Pick<Props, never>, never> & Pick<InferProps<{ foo: Validator<number>; bar: Validator<(...args: any[]) => any>; }>, "foo" | "bar">>': foo, bar



md5-e744aa0bac3ac7f45f015259c5597ea9



Type 'ConnectedComponentClass<...>' is not assignable to type 'ComponentType<{}>'.
Type 'Readonly<{}>' is missing the following properties from type 'Readonly; bar: Validator<(...args: any[]) => any>; }>>': foo, bar

And even if typing everything explicitly helped, I agree with @alexandrudanpop that it should be possible to infer those types instead of doing everything manually 🤔 

**Edit:** for anyone looking for inspiration for a workaround, I currently resort to explicitly casting using `unknown`:
```ts
// assumes `mapDispatchToProps` is an object, could be a function if used with `ReturnType`
type OwnProps = Omit<Props, keyof ReturnType<typeof mapStateToProps> | keyof mapDispatchToProps>
connect(mapStateToProps, mapDispatchToProps)(Foo) as unknown as React.ComponentType<OwnProps>

Something that might help with inference (still some manual work tho) is doing something like this
Going off of @kdmadej example

type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
type OwnProps = {
  a: string;
  b: number;
}

type Props = StateProps & DispatchProps & OwnProps;

const Foo = (props : Props) => <></>

const mapStateToProps = (state : any) => ({
  foo: 1
})

const mapDispatchToProps = {
  bar: () => {}
}

export default connect(mapStateToProps, mapDispatchToProps)(Foo)


// In another file
const Baz = () => <Bar /> <-- this should throw error expecting `a` and `b`

The problem that I'm facing however, is when I add OwnProps to the mix. Using <Bar /> anywhere should throw an error because of missing props a and b, but it isn't, and it's really bugging me (no pun intended).

I got around this using this

const ConnectedBar: React.ComponentType<OwnProps> = connect(mapStateToProps, mapDispatchToProps)(Bar);
export default ConnectedBar;

or equivalently

export default connect(mapStateToProps, mapDispatchToProps)(Foo) as React.ComponentType<OwnProps>;

I've fixed my issues using React.FC<Props> to properly type the function component 😉
Updated the original post

Any updates on this? As @alexandrudanpop said, adding all those types instead of having some basic type inference does not seem like a good solution. I'm using 7.2.1 and I still have the same issue.

In my other components I have:

const connector = connect(mapStateToProps, mapDispatchToProps);
type PropsFromRedux = ConnectedProps<typeof connector>;
type Props = PropsFromRedux & OwnProps;

but this doesn't seem to work if the parent is also sending some props.

Also experiencing the same issue.
Started breaking after npm update.
My fix is as follows:
export default connect(mapStateToProps)(ConnectedIntlProvider) as unknown as ComponentType<unknown>;
As unknown because no OwnProps. There should only OwnProps go.

However, this way it works:
export default connect<StateFromProps, null, OwnProps, ApplicationState>(mapStateToProps)(viewWithIntl);

But this way it does not:
export default connect<StateFromProps, DispatchFromProps, OwnProps, ApplicationState>( mapStateToProps, mapDispatchToProps, )(attachmentFormWithIntel)
And it requires the suffix:
as unknown as ComponentType<OwnProps>;

Using
"@types/react": "16.9.49",
"@types/react-redux": "^7.1.9",
"typescript": "^4.0.3"
"redux": "^4.0.5",
{
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.1",
"redux": "^4.0.5",
},
"devDependencies": {
"@types/react": "16.9.49"
"@types/react-dom": "16.9.8",
"@types/react-redux": "^7.1.9",
"typescript": "^4.0.3"
}
}

Was this page helpful?
0 / 5 - 0 ratings