Property is missing in type '{ [x: string]: string; }'

Viewed 10000

In this react app there is a form with a few input fields. These fields all use this.handleChange with the onChange attribute.

private handleChange = (event: React.FormEvent<HTMLInputElement>) => {
    let target = event.target as HTMLInputElement;
    this.setState({
        [target.name]: target.value
    });
};

The typescript error I get is:

ERROR in [at-loader] ./src/components/FormSubmitHighScore.tsx:43:23 
TS2345: Argument of type '{ [x: string]: string; }' is not assignable to parameter of type 'Pick<State, "first_name" | "last_name" | "email_address" | "school_name" | "region" | "submitted">'.
Property 'first_name' is missing in type '{ [x: string]: string; }'.

So by setting the state name field dynamically (based on what input is being used) this Typescript error gets triggered (but it does work in the browser).

How would I specify a correct type in this context?

4 Answers

Unfortunately until these bugs are fixed - you have to go with some ugly workarounds like:

this.setState({
    [target.name]: target.value
} as any);

Passing a function to setState works too. My guess is that it is because you are spreading state, so typescript knows it has all the properties of state.

    this.setState(state => ({
      ...state,
      [name]: value,
    }));

I was getting a similar error to what you were getting. Here is my implementation for a change handler:

  onChange = (name: keyof ComponentState, value: any) => {
    this.setState(state => ({
      ...state,
      [name]: value,
    }));
  };

I had the same issue when i was managing my props using react with typescript as a programming language. I was using CBC so i manage to get a work around on this by setting the state values to be optional. Here is my class based component where i'm getting track of the state of username, email and password:

interface Props {}
interface State {
  username?: string;
  email?: string;
  password?: string;
}
export default class App extends React.Component<Props, State> {
  constructor(props: any) {
    super(props);
    this.state = {
      username: "",
      password: "",
      email: "",
    };
  }

  onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
  };
  onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    this.setState((state) => ({
      ...state,
      [name]: value,
    }));
  };

  render(): React.ReactNode {
    const { username, password, email } = this.state;
    return (
      <div className="app">
        <form onSubmit={this.onSubmit}>
          <input
            type="text"
            placeholder="username"
            name="username"
            value={username}
            onChange={this.onChange}
          />
          <input
            type="text"
            placeholder="email"
            name="email"
            value={email}
            onChange={this.onChange}
          />
          <input
            type="text"
            placeholder="password"
            name="password"
            value={password}
            onChange={this.onChange}
          />
          <button type="submit">register</button>
        </form>
      </div>
    );
  }
}

By setting fields optional in the State type you don't need to do this:

this.setState({
    [name]: value
} as any);

Because typescript knows how to handle this.

Related