Почему состояние только для чтения в родительском классе, если я расширяю дочерний класс из родительского в React (Typescript)? - PullRequest
0 голосов
/ 17 октября 2018

Я создал интерфейс для родительского класса, определенного для 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>;
  }
}

Может кто-нибудь объяснить это поведение.

PS

У меня есть еще пара вопросов.

  1. Кроме того, в библиотеке React, когда я открываю файл index.d.ts, состояние на самом деле только для чтения .Как это работает в классе Test, упомянутом ранее?

  2. Если я не упомяну тип для реквизита в конструкторе в классе Test, возникает ошибка компиляции, говорящая

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

    Почему он не принимает тип {}, указанный в определении класса?

1 Ответ

0 голосов
/ 18 октября 2018

Полученная ошибка выглядит примерно так:

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

Это говорит о том, что если у вас есть переменная типа Readonly<S>, тогда присваивать значение типа * 1005 небезопаснок этому.Давайте посмотрим, почему.

Поскольку 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.Отсюда и ошибка.

На практике трудно присвоить литеральное значение переменной общего типа.Даже в тех редких случаях, когда вы можете придумать что-то, что работает для всех возможных реализаций параметра универсального типа, компилятор, скорее всего, выдаст вам ошибку, потому что ситуация слишком редкая, чтобы ее можно было проверить, и вам потребуетсясделать утверждение типа .Действительно, вы можете обойти эту проблему, выполнив такое утверждение:

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

, но это небезопасно по причине, указанной выше.В идеале вы должны изменить свой код, чтобы он не требовал установки state, пока у вас не будет конкретного типа для S.Возможно, сделав Parent abstract класс?

В любом случае, надеюсь, это поможет.Удачи!

...