如果我在 React (Typescript) 中从 Parent 扩展 Child class,为什么 state 在 Parent class 中是只读的?

Why is state readonly in Parent class if I extend a Child class from Parent in React (Typescript)?

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

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

谁能解释一下这种行为。

P.S。

我还有几个问题。

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

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

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

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

您得到的错误类似于:

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

这告诉你,如果你有一个 Readonly<S> 类型的变量,那么给它赋一个 { 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<ProblemChildState>,但如您所见,值 { name: '' } 根本不是有效的 ProblemChildState,因为它缺少 naughtiness 属性.

由于该赋值对于具体 ProblemChildState 无效,因此该赋值对于泛型 S 必定无效。因此错误。

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

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

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

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