I'm using React 18 with Redux 8 and Typescript. I want to create a component that allows the caller to pass in one of the arguments, but the other two arguments are derived from state. So I created this file ...

type OrderItemComponentProps = {
  contact: Contact | undefined;
  isAuthenticated: boolean;
  orderItem: OrderItem;
}

const OrderItemComponent: React.FC<OrderItemComponentProps> = ({ contact, isAuthenticated, orderItem }) => {
    ...
}

function mapStateToProps(state: RootState, ownProps: OrderItemComponentProps): OrderItemComponentProps {
  return {
    contact: ownProps.contact, // passed as a prop to the container
    isAuthenticated: state.auth.isAuthenticated, // derived from Redux state
    orderItem: state.orderItem.orderItem // derived from Redux state
  }
}

export const OrderItemContainer = connect(mapStateToProps)(OrderItemComponent)

In a separate file, I invoke the above using

<OrderItemContainer contact={orderItem.sender} />

But this generates the Typescript error

Type '{ contact: Contact | undefined; }' is missing the following properties from type '{ contact: Contact | undefined; isAuthenticated: boolean; orderItem: OrderItem; context?: Context<ReactReduxContextValue<any, AnyAction>> | undefined; store?: Store<...> | undefined; }': isAuthenticated, orderItem

What is the right way to type my "mapStateToProps" function so properly allow a declaration of this component?

1

There are 1 best solutions below

0
Andrew On

Unfortunately, the type definition of connect is weird. The most straightforward way to approach this is to split the type definition between what is returned from mapStateToProps and parent props. Luckily, ReturnType allows you to infer the interface of mapStateToProps. Then use type casting to force the resulting component to only need the parent props, which is contact. It should look something like this

type OrderItemComponentProps = {
  contact: Contact | undefined;
}

const OrderItemComponent: React.FC<OrderItemComponentProps & ReturnType<typeof mapStateToProps> = ({ contact, isAuthenticated, orderItem }) => {
    return null
}

function mapStateToProps(state: RootState) {
  return {
    isAuthenticated: state.auth.isAuthenticated, // derived from Redux state
    orderItem: state.orderItem.orderItem // derived from Redux state
  }
}

export const OrderItemContainer = connect(mapStateToProps)(OrderItemComponent) as React.FC<OrderItemComponentProps>