1

Similar to this in Javascript.

I have a React component that has an interface:

interface IState {
  email: string,
  passwordOne: string,
  passwordTwo: string,
  error: {}
}

class SignUpForm extends React.Component<{}, IState> {
  constructor(props) {
    super(props)
  }
...

onChange = event => {
  this.setState({ [event.target.name]: event.target.value });
};
...

<input type="email" placeholder="Your email address" name="email"
    value={email} onChange={this.onChange}/>
...

The setState is complaining that Argument of type is not assignable to IState.

Is there a way to fix this, or is this not possible in TS?

rmcsharry
  • 5,363
  • 6
  • 65
  • 108

2 Answers2

3

You should never spread this.state in a setState call because setState happens asynchronously and you could end up with some significant and hard to track race condition issues.

If you want to use the hacky way of tricking the type system, you can use:

  onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    let key = event.target.name;
    let value = event.target.value;
      this.setState(state=>({...state, [key]: value }));
  };

or just ignore the typescript error as that's essentially what you are doing:

  onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    let key = event.target.name;
    let value = event.target.value;
    // @ts-ignore
    this.setState({[key]: value });
  };

If you want to solve it without type issues, use the solution from this comment: https://github.com/Microsoft/TypeScript/issues/13948#issuecomment-412365629

private handleChange = <T extends keyof IState>(event: React.ChangeEvent<HTMLInputElement>) => {
  const newState = {
    [event.target.name]: event.target.value,
    // keyNotInState: '42', -> would throw a compile time error
    // numericKeyInState: 'assigning wrong type' -> would throw a compile time error
  };
  this.setState(newState as { [P in T]: IState[P]; });
}
Zachary Haber
  • 10,376
  • 1
  • 17
  • 31
1

i believe you can hack it into working like this... if is failing because IState expects all of the properties and you are just setting one.

onChange = event => {
  this.setState({ ...this.state, [event.target.name]: event.target.value });
};
Aaron Saunders
  • 33,180
  • 5
  • 60
  • 80