Xelad1
Xelad1

Reputation: 175

Unable to validate prop types with Typescript

I'm a little unsure as to how I'm supposed to add my props for type checking within a component. Here is my component thus far:

import * as React from 'react';
import { connect } from 'react-redux';

export namespace CalculatorDisplay {
  export interface Props {
    display: object,
    calculatorDisplay: string,
  }

  export interface State {

  }
}

// TODO: How do we get displayString to be properly recognized in props validation?
export default class CalculatorDisplay extends React.Component<CalculatorDisplay.Props, CalculatorDisplay.State> {

  render() {

    let calculatorDisplay = this.props.display.displayString;
    return (
      <div className="display">
        <div className="displayText">
          { calculatorDisplay }
        </div>
      </div>
    )
  }

}

The error I'm getting here is:

ERROR in [at-loader] ./src/components/CalculatorDisplay.tsx:23:48 
    TS2339: Property 'displayString' does not exist on type 'object'.

ERROR in [at-loader] ./src/containers/App.tsx:22:24 
    TS2322: Type '{ display: any; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<CalculatorDisplay> & Readonly<{ children?: ReactNo...'.
  Type '{ display: any; }' is not assignable to type 'Readonly<Props>'.
    Property 'calculatorDisplay' is missing in type '{ display: any; }'.

I understand (I think) that TS is trying to validate something more specific than a data type of an object (perhaps the actual state object itself?).

The only way I seem to be able to get this error to not be thrown is by setting display to type of 'any' but that doesn't really give us any actual detail.

My other assumption would be that I could set calculatorDisplay to a type of string independently (as that is what we want it to return, and ultimately all that I care about), which is what I've done above.

This is how CalculatorDisplay is being used in App.tsx:

import * as React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Calculator from '../components/Calculator'
import CalculatorDisplay from '../components/CalculatorDisplay'
import * as CalculatorActions from '../actions'
import { RouteComponentProps } from 'react-router'

export namespace App {
  export interface Props extends RouteComponentProps<void> {
    actions: typeof CalculatorActions;
  }

  export interface State {
    /* empty */
  }
}

const App = ({todos, actions, display}) => (
  <div>
    <CalculatorDisplay display={display}/>
    <Calculator actions={actions}/>
  </div>
)

const mapStateToProps = state => ({
  todos: state.todos,
  display: state.numberpad
})

const mapDispatchToProps = dispatch => ({
    actions: bindActionCreators(CalculatorActions as any, dispatch)
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

Would love some insight into how I'm approaching this incorrectly. Thank you.

Upvotes: 0

Views: 3536

Answers (1)

Nick
Nick

Reputation: 626

You need to shape the display property of your props interface to how it should look. Take a look at your numberpad object in your redux store, to figure out what display should look like. Here's how I believe it should look given your component. Note: I've removed the namespace as there is no need for it in this component.

export interface Props {
    display: {
        displayString: string,
        // Other properties of display go here.

    };
    calculatorDisplay: string;
}

export interface State {}

export default class CalculatorDisplay extends React.Component<Props, State> {
    render() {

        const calculatorDisplay = this.props.display.displayString;
        return (
            <div className="display">
              <div className="displayText"> { calculatorDisplay }</div>
            </div>
    )
    }

}

Upvotes: 1

Related Questions