score:4

Accepted answer

your problem is a common problem people encounter when switch from js to ts with react and using hoc like connect with their component.

basically, with your interface ifooprops, you tell ts that userid is a required prop whenever the component is used and ts doesn't know that actually, userid will be taken care of / injected to the props through connect.

there is couple of way to resolve the problem, the first is making userid an optional attribute.

interface ifooprops{
  userid?: number
}

this approach does have a downsize, as ts will ask yo to perform null/undefined check whenever you use userid. however, it's the most simple one.

the other approach is to define another interface, in your example, can be conventionally named: ifooinjectedprops. your component will be like:

interface ifooprops {
   // ....whatever props that require by the component
}

// all injected props from the store
interface ifooinjectedprops extends ifooprops {
  userid: number
}

class foo extends component<ifooprops> {
    get injectedprops() {
       // cast this.props as injectedprops to bypass typechecking 
       return this.props as ifooinjectedprops;
    }

    render() {
      // get userid from the getter `injectedprops`
      const { userid } = this.injectedprops;

      return <p>foo...</p>
    }
}

score:-1

because you said that "ifooprops" as type of props it is expecting you to send userid when you use it. set the type as any. you dont need to set the type of props when you using redux.

class foo extends component<any>{
   render(){
     return <p>foo...</p>
   }
}

score:0

after a few years of working with typescript and redux i managed to find my ultimate solution for connect. it's a little tricky

first, you need to use a module augmentation feature from typescript. it allows you to extend existing typings. we would use it to extend a react-redux typings. you need to create a .ts file somewhere in your project with the following code:

import { component, componentclass } from "react-redux";

declare module "react-redux" {
  export interface inferablecomponentenhancerwithprops<tinjectedprops, tneedsprops> {
    <p extends tinjectedprops>(component: component<p>): componentclass<
      omit<p, keyof tinjectedprops> & tneedsprops
    > & {
      wrappedcomponent: component<p>;
    };

    allprops: tinjectedprops & tneedsprops;
  }
}

here we add to the existing inferablecomponentenhancerwithprops interface a property allprops: tinjectedprops & tneedsprops;, the rest of the code is a rewrite of the existing interface declaration from the original typings.

after creating it typescript compiler should automatically pick up the new definition and now we are ready to go back to your files

foo.tsx

// you only define props that you would like to pass from the parent component, not the props that are injected from redux state
interface ifooprops {
  testprop: any;
}

// iconnectprops are defined at the bottom
class foo extends component<iconnectprops>{
    render() {
      this.props.testprops;
      this.props.userid;
      return <p>foo...</p>
    }
}

const mapstatetoprops = (state: istate, props: ifooprops) => {
  return {
    userid: state.user.id,
    ...props,
  }
}

const mapdispatchtoprops = (dispatch: dispatch) => {
  return {}
}

const connectcreator = connect(
  mapstatetoprops,
  mapdispatchtoprops
);

// here we use our new typings, thanks to them typescript automatically merge 
// props that we declared in mapstatetoprops so the iconnectprops 
// looks like {
//  userid: string;
//  testprop: any;
// }
type iconnectprops = typeof connectcreator.allprops;

export default connectcreator(foo);

index.tsx

// typescript only tells you to pass a testprop
<foo testprop={5} /> 

Related Query

More Query from same tag