【问题标题】:Why is state readonly in Parent class if I extend a Child class from Parent in React (Typescript)?如果我在 React (Typescript) 中从 Parent 扩展 Child 类,为什么状态在 Parent 类中是只读的?
【发布时间】:2019-03-22 10:12:36
【问题描述】:

我已经为 State 定义的 Parent 类创建了一个接口:

interface ParentState { name: string; }

我创建了一个具有上述类型状态的组件:

class Test extends React.Component<{}, ParentState> {
  constructor(props: any) {
    super(props);

    // Works fine!
    this.state = { name: '' };
  }
}

这是按预期工作的。但是,如果我创建一个父组件和一个子组件,其中子组件从父组件继承,子组件从父组件继承,则父组件的状态变为只读。

interface ChildState extends ParentState {}

class Parent<P, S extends ParentState> extends React.Component<P, S> {
  constructor(props: P) {
    super(props);

    this.state = { name: '' };
    ^^^^^^^^^^
    // Compilation error!
    // TS2322: Type '{}' is not assignable to type 'Readonly<S>'.
  }

  render() {
    return <div>parent</div>;
  }
}

class Child extends Parent<{}, ChildState> {
  constructor(props: ChildProps) {
  super(props);

    // Works fine!
    this.state = { name: '' };
  }

  render() {
    return <div>child</div>;
  }
}

谁能解释一下这种行为。

附:

我还有几个问题。

  1. 另外,在 React 库中,当我打开 index.d.ts 文件时,state 实际上是 readonly。这在前面提到的 Test 类中是如何工作的?

  2. 如果我在 Test 类的构造函数中没有提及 props 的类型,则会出现编译错误提示

    TS7006: Parameter 'props' implicitly has an 'any' type.
    

    为什么不采用类定义中提到的类型{}

【问题讨论】:

  • 最后一点(#1)很简单:readonly 属性应该在构造函数中被初始化。
  • 你有多确定this.state = {}Test 的情况下没问题,并且不会告诉你“property 'name' is missing in type '{}'”?
  • @jcalz 对不起,我的错。我现在已经编辑了这个问题。我的目的是证明 this.state 在 Test 类中仍然是可分配的。但它在 Parent 类中的工作方式不同。
  • #2:因为子类的构造函数不需要看起来像父类的构造函数,所以编译器不知道props参数必须是什么类型。

标签: reactjs typescript generics inheritance covariance


【解决方案1】:

你得到的错误是这样的:

Type '{ name: string }' is not assignable to type 'Readonly<S>'.

这告诉你,如果你有一个Readonly&lt;S&gt; 类型的变量,那么给它分配一个{ name: string } 类型的值是不安全的。让我们看看为什么。

由于S 是通用的,因此您使用它编写的代码需要与用任何有效的具体类型替换S 兼容。 S 的约束是 S extends ParentState,所以让我们想出一个满足该约束的类型并用它实例化 Parent

interface ProblemChildState extends ParentState {
  naughtiness: true
}
const problemComponent = new Parent<{}, ProblemChildState>({});

让我们想象一下构造函数在做什么,现在 S 已被具体指定为 ProblemChildState

this.state = { name: '' }; // oops!

this.state 的类型是Readonly&lt;ProblemChildState&gt;,但正如您所见,{ name: '' } 的值根本不是有效的ProblemChildState,因为它缺少naughtiness 属性。

由于该分配对于具体ProblemChildState 无效,因此该分配对于通用S 必须无效。因此出现错误。

在实践中,很难将文字值分配给泛型类型的变量。即使在极少数情况下您可以想出适用于泛型类型参数的所有可能实例化的东西,编译器也可能会给您一个错误,因为这种情况太罕见而无法检查,您需要做一个type assertion。事实上,你可以通过这样的断言来解决这个问题:

this.state = { name: '' } as Readonly<S>; // not safe!

但由于上述原因,这并不安全。理想情况下,您应该更改代码以不需要设置state,直到您拥有S 的具体类型。可能通过将Parent 设为abstract 类?

无论如何,希望对您有所帮助。祝你好运!

【讨论】:

    猜你喜欢
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 1970-01-01
    • 2020-05-07
    • 2010-11-13
    • 2014-04-05
    • 1970-01-01
    相关资源
    最近更新 更多