Josh
Josh

Reputation: 1235

React component throwing TypeScript error for function passed in as prop

I have a Class based React Component that accepts a function as a prop.

The component was originally a stateless functional component that accepted the same function without any TypeScript errors.

Now that I've converted it to a class component I'm getting an error when I call the function: Property 'onRouteChange' does not exist on type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>'

As well as an error when I de-structure it in the props: Property 'onRouteChange' is missing in type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>' but required in type '{ onRouteChange: any; }'.

I initially tried to define it in the types declaration at the top of the file but that's not working either.

Component

type SignInState = {
  signInEmail: string,
  signInPassword: string
}

class SignIn extends Component<{}, SignInState> {
  constructor(props: {}){
    super(props);
    this.state = {
      signInEmail: '',
      signInPassword: ''
    }
  }

  onEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      this.setState({ signInEmail: e.target.value })
  }

  onPasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ signInPassword: e.target.value })
  }

  onSubmitSignIn = () => {
    // fetch()
    console.log(this.state);
// TS Error: Property 'onRouteChange' does not exist
    this.props.onRouteChange('home');
  }
  
  render() {
// TS Error: Property 'onRouteChange' is missing in type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>' 
    const { onRouteChange } : { onRouteChange: any} = this.props

    return (
      <Container>
        <Main>
          <Form>
            <Fieldset>
              <SignInTitle>Sign In</SignInTitle>
              <EmailAddressSection>
                <Label>Email</Label>
                <EmailInput />
              </EmailAddressSection>
              <PasswordSection>
                <Label>Password</Label>
                <PasswordInput />
              </PasswordSection>
            </Fieldset>
            <div>
              <SignInButton
                onClick={this.onSubmitSignIn}
                type="submit"
                value="Sign in"
              />
            </div>
            <AccountOptionsSection>
              <AccountOption onClick={() => onRouteChange("register")}>
                Register
              </AccountOption>
            </AccountOptionsSection>
          </Form>
        </Main>
      </Container>
    );
  }
};

export default SignIn;

Upvotes: 1

Views: 3580

Answers (2)

Bens Steves
Bens Steves

Reputation: 2849

Create a type or an interface for your props just as you did with your state.

Props Interface

// interface over type literal
interface IProps { 
 onRouteChange: (param: string) => void
}

Avoid Typing to any

Try to avoid typing something to any. You are basically telling typescript this has no type. That defeats the purpose of TS. If you truly do not know, type it to unknown.

But, you are passing a string home so you have some information on it that you can use to type it a little better.

Here, I am guessing onRouteChange is a function that accepts one parameter of type string and returns nothing (does a side effect). You can update this accordingly.

Typing Your Component Props

You do exactly what you did with your state type (prefer interface over types).

class SignIn extends Component<IProps, IState>

Now you should not have to type onRouteChange when you destructure it.

const { onRouteChange } : { onRouteChange: any} = this.props

becomes

const { onRouteChange } = this.props

Upvotes: 1

Rohan Agarwal
Rohan Agarwal

Reputation: 2609

class SignIn extends Component<{}, SignInState> 

The first param to Component type is props type , and you have not defined it, therefore the error.

Define a interface,

interface ISignInProp {

onRouteChange:any
}

and pass it to your Component

class SignIn extends Component<ISignInProp , SignInState> 

Upvotes: 1

Related Questions