1

I have a typescript react/ redux component which won't compile. I have other components that are almost exactly the same and do compile. I need to understand why this one is not compiling.

Non-compiling component source (in full):

import * as React from 'react';
import { connect } from 'react-redux';
import { ApplicationState } from '../store';

interface ToastProps {
    messages: string[]
}

 interface FadeState {
    visible: boolean
}

class ToastBar extends React.Component<ToastProps, FadeState> {
    public render() {
        if (this.props.messages.length > 0) {
            this.state = { visible: true };
        } else {
            this.state = { visible: false };
        }
        let key = 0;
        return <div id="toastbar" className={this.state.visible ? 'show' : ''}>
            {this.props.messages.map(function (listValue) {
                return <p key={++key}>{listValue}</p>;
            })}            
        </div>;
    }
}

const mapStateToProps = (state: ApplicationState, ownProps?: ToastProps) => ({
    messages: state ? state.toastMessages.messages : []
});

export default connect(mapStateToProps, {}, null, { pure: false })(ToastBar);

I get this error message from the IDE and from running an npm run-script build:

(9,82): Argument of type 'typeof ToastBar' is not assignable to parameter of type 'ComponentType>'. Type 'typeof ToastBar' is not assignable to type 'StatelessComponent>'. Type 'typeof ToastBar' provides no match for the signature '(props: Shared<{ messages: String[]; }, ToastProps> & { children?: ReactNode; }, context?: any): ReactElement | null'.

Here is another component which compiles fine:

import * as UserStore from '../store/UserManagement';
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { ApplicationState } from '../store';
import { connect } from 'react-redux';
import { User, UserRole } from '../model/User';
import { UserEditDialog } from './UserEditDialog';

interface UserMgmtState {
    isLoading: boolean;
    users: User[];
    currentUser: User;
}

interface UserEditState {
    editingUser: User | null;
}

type DispatchProps = typeof UserStore.actionCreators;
type UserManagementProps = UserMgmtState & DispatchProps & RouteComponentProps<{}>;

class UserManagement extends React.Component<UserManagementProps, UserEditState> {

    componentWillMount() {
        // snipped
    }

    editUser(user: User) {
        this.setState({
            editingUser: user
        }); 
    }

    stopEditingUser() {
        this.setState({
            editingUser: null
        });
    }

    deleteUser(user: User) {
        if (confirm('Delete ' + user.fullName + '. Are you sure?'))
            this.props.deleteUser(user.name);
    }

    userIsCurrentUser(user: User): boolean {
        return this.props.currentUser.name == user.name;
    }

    saveUserEdit(user: User) {
        this.props.updateUser(user);
        this.setState({
            editingUser: null
        });
    }

    displayRoles(roles: UserRole[]): string {
        return roles.join(', ');
    }

    renderUsersTable(): any {
        if (this.props.isLoading) {
            return <div>Loading users</div>;
        }
        else {
            return <div>
                <!-- snipped -->
             </div>;
        }
    }

    public render() {
        return <div>
            <h1>User Management</h1>
            {this.renderUsersTable()}
        </div>;
    }
}

const mapStateToProps = (state: ApplicationState, ownProps?: UserMgmtState) => ({
    isLoading: state ? state.userManagement.isLoading : false,
    users: state ? state.userManagement.users : [],
    currentUser: state ? state.loggedInUser.currentUser : null
});

export default connect(
    mapStateToProps, // Selects which state properties are merged into the component's props
    UserStore.actionCreators // Selects which action creators are merged into the component's props
)(UserManagement);

Why does UserManagement compile, and Toastbar not? I am really mystified here...

1 Answer 1

1

The problem was that the state.toastMessages.messages property had the type String[], and the ToastProps.messages property had the type string[]

These are different types so TypeScript didn't consider the result of the mapStateToProps method to have the same type as the ToastProps interface.

In case anyone else has a similar problem and wants to know how to troubleshoot: To figure this out, I changed the one-liner mapStateToProps method to a multi-line method defining and returning a result, then the compile problem showed up there. Once I knew that the output of the mapStateToProps had an incompatible type I checked the type of state.toastMessages.messages and found the problem

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.