【问题标题】:TypesScript wrongly assumes that property is read-only打字稿错误地假定属性是只读的
【发布时间】:2017-06-22 19:15:24
【问题描述】:

我有一个使用 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:无法分配给“值”,因为它是常量或 只读属性。

这个错误让我想到这可能是 typescript 执行不改变状态规则的方式。为了验证这个理论,我做了以下事情。

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 的第一个sn-p 代码中一样? 其次,为什么将我的状态对象嵌套更深一层可以解决这个问题?

【问题讨论】:

  • 那行得通。与state[e.target.name] = e.target.value 相同。我只是想知道为什么 object.assign 还不够。

标签: javascript reactjs typescript


【解决方案1】:

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

我不能保证这就是实际发生的事情,因为这对我来说是个新闻。但这实际上是好消息,而不是坏消息。

你使用的是什么版本的东西?

此外,如果您要在下一个主要版本中继续使用它,您需要养成使用 setState 的函数形式的习惯。

【讨论】:

  • 我使用 create-react-app 创建了这个应用程序,所以我看不到 typescript 的版本,但我猜它是 2.3.4 或那里的某个地方
  • 我刚刚弹出了它的 typescript 2.3.3
【解决方案2】:

你可以改写为:

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;
}

我用“只读”标记类型中的所有属性(用于道具、状态、redux 等)。我想要所有人的不可变模式:)

【讨论】:

  • 为什么不只是 type State = { value: string }; const state: Readonly&lt;State&gt;; TS 2.1 为您提供了一个非常有用的通用构造函数,而无需到处编写它。
  • 我认为类型定义中的 readonly 更好。当我使用Readonly&lt;T&gt;时,我必须经常写这个语句。
【解决方案3】:

如果查看 React 的 Component 的类型定义,可以看到 state 属性定义为 Readonly&lt;S&gt;,其中 S 是状态类型参数。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多