typescript@next does not fix the issue.
Going off issue #21417
Code
I define the component class and connect it as follows:
export class SkypeConversationArea extends React.Component<Props> {..}
const mapStateToProps = (state: State) => {
return {
loading: state.loading,
hangUp: state.conversation.hangUp,
selfParticipantStatus: state.conversation.selfParticipant.status
}
}
const mapDispatchToProps = (dispatch: any) => {
return {
saveRef: (ref: HTMLElement) => {
dispatch(saveConersationAreaRef(ref))
},
setFullscreen: (set: boolean) => {
dispatch(setFullscreen(set))
}
}
}
type ReceivedProps = {
lifecycleEndpointNotifyConfigured: (event: LIFECYCLE_EVENT) => any
};
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = ReturnType<typeof mapDispatchToProps>;
type Props = StateProps & DispatchProps & ReceivedProps;
export default connect<StateProps, DispatchProps, ReceivedProps>(mapStateToProps, mapDispatchToProps)(SkypeConversationArea);
When I attempt to use the component in a parent component's render function, and pass in only "receivedProps", as such:
<SkypeConversationArea lifecycleEndpointNotifyConfigured={props.lifecycleEndpointNotifyConfigured} />
I get the error
TS2322: Type '{ lifecycleEndpointNotifyConfigured: (event: string) => any; }' is not assignable to type 'Readonly
My understanding is that I should be able to pass props from the parent and not have to worry about including the props in mapstatetoprops and mapdispatchtoprops. I believe that this is a bug, but I'm new to react-redux-typescript so not sure.
I see the same issue. A workaround is to cast the component with the appropriate props:
interface IFooContainerProps {
alpha: string;
}
interface IFooDispatchProps {
beta: () => void;
}
interface IFooState {
charlie: string;
}
interface IFooInnerProps {
alpha: string;
beta: () => void;
charlie: string;
}
const mapStateToProps = (state: IFooState, props: IFooContainerProps) => {
return {
alpha: props.alpha,
charlie: state.charlie,
};
};
const mapDispatchToProps = (dispatch: Dispatch<any>, props: IFooContainerProps) => {
return {
beta: () => {
dispatch({ type: "foo" });
},
};
};
@connect<
IFooContainerProps,
IFooDispatchProps,
IFooInnerProps,
IFooState
>(mapStateToProps, mapDispatchToProps)
class FooContainer extends React.PureComponent<IFooInnerProps, {}> {
public render(): React.ReactNode {
const {
props: {
alpha,
beta,
charlie,
},
} = this;
return (
<div onClick={beta}>{alpha} {charlie}</div>
);
}
}
export default FooContainer as React.ComponentClass<IFooContainerProps>;
Ouch...Just started using TypeScript in an existing React project and keep running up against this kind of nonsense. TS is by nature declarative, but it is ridiculous the number of complex workarounds I need to get past this:
TS2322: type '{}' is not assignable to type 'y' When 'y' contains {...props}
OP: Have you tried rewriting Props as
type Props = Partial<StateProps> & Partial<DispatchProps> & ReceivedProps;
?
When used Partial and if the IFooContainerProps interface is
interface IFooContainerProps {
alpha: {
beta: number;
}
}
It will be got an error message:
[ts] Object is possibly 'undefined'.
Another workaround that I found is to pass a mergeProps function as the third param to connect HOC:
const mergeProps = (
stateProps: ComponentStateProps,
dispatchProps: ComponentDispatchProps,
ownProps: ComponentOwnProps,
) => ({
...ownProps,
...stateProps,
...dispatchProps,
});
...
export default connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
)(Component);
Not sure if this approach has any downsides, but it worked for me
Getting a similar error when passing error to the provider:
import * as React from "react";
import { Provider } from "react-redux";
import { BrowserRouter as Router, Route } from "react-router-dom";
import { App } from "./App";
export interface RootProps {
store: object;
}
const Root = ({ store }: RootProps) => (
<Provider store={store}>
<Router>
<Route path="/" component={App} />
</Router>
</Provider>
);
export default Root;
Not sure if it is the same issue or if I am doing something wrong
with current typescript v3.2.2, I got similar problem when tried to use React.createContext api to inject some props to another component class, and here is my code:
export interface IContext {
width: number;
height: number;
}
export const Context = React.createContext<IContext | null>(null);
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// the un-typed primitive method
export function inject(Comp) {
return function injectedComponent(props) {
return (
<Context.Consumer>
{ctx => <Comp {...props} {...ctx} />}
</Context.Consumer>
);
}
}
// the typed implementation, which is my concern
export function injectContext<P extends IContext, R = Omit<P, 'width' | 'height'>>(
Component: React.ComponentClass<P>
): React.SFC<R> {
return function injectedComponent(props: R) {
return (
<Context.Consumer>
{(ctx: IContext) => <Component {...props} {...ctx} />}
</Context.Consumer>
);
};
}
here is the error snapshot:

I'm quite confused why this is not working. the generic R is P omitted with IContext props, so why does R & IContext not assignable to P?
Another workaround that I found is to pass a
mergePropsfunction as the third param toconnectHOC:const mergeProps = ( stateProps: ComponentStateProps, dispatchProps: ComponentDispatchProps, ownProps: ComponentOwnProps, ) => ({ ...ownProps, ...stateProps, ...dispatchProps, }); ... export default connect( mapStateToProps, mapDispatchToProps, mergeProps, )(Component);Not sure if this approach has any downsides, but it worked for me
Thanks for the suggestions, it also works for me. :-D
Update: The error reported by the OP no longer occurs (and was fixed as part of broader Readonly/mapped type assignability fixes. But for anyone still looking at this thread - the code in the OP is a little out of date - connect needs a 4th type argument to be correctly typed (State).
Most helpful comment
Another workaround that I found is to pass a
mergePropsfunction as the third param toconnectHOC:Not sure if this approach has any downsides, but it worked for me