0
votes

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

1 Answers

0
votes

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