TypesScript错误地认为该属性是只读的

时间:2017-06-22 19:15:25

标签: javascript reactjs typescript

我有一个简单的反应组件,我用typescript创建,我遇到了以下奇怪的错误。这是我的代码。

interface State {
  value: string
}

class App extends React.Component<{}, State> {
  constructor() {
    super();
    this.state = {
      value: ''
    }
  }

  changeHandler = (e: any) => {
    let state = Object.assign({}, this.state);
    state.value = e.target.value;
    this.setState(state);
  }

  render() {
    return (
      <div className="App">
        <input
          type="text"
          value={this.state.value}
          name="value"
          onChange={this.changeHandler} />
      </div>
    );
  }
}

export default App;

这就是我得到的错误。

  

错误TS2540:无法分配给'value',因为它是常量或a   只读属性。

这个错误让我想到也许这是打字稿执行不改变状态规则的方式。为了验证这个理论,我做了以下几点。

this.state.value = e.target.value

在这种情况下,我显然是直接改变状态,并且确定我得到了相同的错误。

然后我有了改变我的界面的想法。

interface State {
  value: Ivalue
}

interface Ivalue {
  value: string;
}

然后我重构我的组件以使用这样的新界面。

class App extends React.Component<{}, State> {
  constructor() {
    super();
    this.state = {
      value: {
        value: ''
      }
    }
  }

  changeHandler = (e: any) => {
    let value = Object.assign({}, this.state.value);
    value.value = e.target.value;
    this.setState({value});
  }

  render() {
    return (
      <div className="App">
        <input
          type="text"
          value={this.state.value.value}
          name="value"
          onChange={this.changeHandler} />
      </div>
    );
  }
}

export default App;

确实这个编译了! 我的问题实际上是2个问题。首先,为什么打字稿不满意我的状态副本,就像我使用Object.assign的第一段代码一样? 其次,为什么将我的状态对象进一步嵌套到一个级别来解决这个问题呢?

3 个答案:

答案 0 :(得分:2)

如果Object.assign的内部实现(或TypeScript对其的理解)是复制对象成员的描述符,而不仅仅是字段名称,那么它正在复制{{1}的readonly属性而不仅仅是state.value的键名。

我无法保证实际上发生了什么,因为这对我来说是个新闻。但这实际上是好消息,而不是坏消息。

您使用的是哪些版本的内容?

此外,如果您打算在下一个主要版本中继续使用它,那么您将养成使用state.value功能形式的习惯。

答案 1 :(得分:0)

您可以重写为:

changeHandler = (e: any) => {
    this.setState({value: e.target.value});
}

desctructuring assigment

changeHandler = ({target: {value}}: any) => {
    this.setState({value});
}
在Typescript中,您可以将您的属性标记为只读。是更好的方法,因为直接的不可变状态不是好的模式。您必须为更改状态运行setState()函数。

您的道具和州可能如下所示:

interface State {
   readonly value: string;
}

我用“readonly”标记类型中的所有属性(用于props,state,redux等)。我想要所有人的永恒模式:)

答案 2 :(得分:0)

如果查看React组件的类型定义,可以看到state属性定义为Readonly<S>,其中S是状态类型参数。

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L209