Definitelytyped: React.d.ts Только для чтения<t>в состоянии и реквизит</t>

Созданный на 25 янв. 2017  ·  91Комментарии  ·  Источник: DefinitelyTyped/DefinitelyTyped

Привет @ericanderson

У меня много проблем с этим изменением при использовании на практике:

Проблема 1: перейти к определению

Когда вы нажимаете «Перейти к определению» свойства реквизита или состояния, Typescript не может его разрешить.

interface MyComponentProps {
    name: string;
}

export abstract class MyComponent extends React.Component<MyComponentProps , void> {
    myMethood() {
       this.props.name; //<-- Go To definition in name
   }
}

image

Имеет смысл, потому что член создан искусственно, но все равно раздражает.

Проблема 2: Иерархии компонентов (Общий P с ограничениями)

Более важно, если вы сделаете абстрактный компонент следующим образом:

interface MyBaseProps {
    onChange?: (val: any) => void;
}

export abstract class MyBase<P extends MyBaseProps> extends React.Component<P, void> {
    myMethood() {
        this.props.onChange!(2); //The type is S["onChange"] instead of (val: any) => void and so is not invocable. 
   }
}

ТС умеет показывать, что есть свойство onChange, но иногда не может обнаружить его тип.

image

Это самое важное изменение , поскольку оно не позволяет мне иметь иерархию компонентов, которые имеют общие реквизиты и функциональные возможности. Похоже на проблему в компиляторе TS, но пока не исправлено.

Проблема 3: Не так только для чтения.

Хотя я согласен с тем, что это изменение хорошо отражает функциональное назначение React, существуют допустимые ситуации, когда вы можете императивно изменить состояние, например, в конструкторе, а также, если вы измените состояние и вызовите forceUpdate, все будет работать нормально.

C# this.state.name = "John"; this.forceUpdate(); //Ok as long as you don't setState afterwards, but calling setState also is annoying with the callback.

Это рекомендуется? Нет.
Это запрещено? Также нет, иначе forceUpdate не будет существовать.

Конечно, вы можете привести состояние к S (или any ) и внести изменения, но если это общий шаблон, это становится громоздким.

Вывод: стоит ли?

Мне грустно, что новая блестящая функция TS создает больше проблем, чем решений в этом случае, но я искренне думаю, что здесь дело обстоит именно так.

С другой стороны, сдача setState отличная 👍 , не знала про Pick<S,K> .

Самый полезный комментарий

Проблема 3 обсуждается, я думаю.

Вы правы в том, что вы можете _технически_ сделать приведенный выше пример в React, но я определенно утверждаю, что это не тот способ, которым React предназначался для использования.

Это можно разбить на 3 отдельных случая.

Общая инициализация

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  public state: State = {
    bar: 5,
  };
}

Инициализация на основе реквизита

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: props.baz,
    };

    // or
    this.setState({
      bar: props.baz,
    });
  }
}

Случайное назначение с forceUpdate

Учитывая, что я считаю, что лучше подтолкнуть людей к «правильным» вещам, вы можете легко обойти эту проблему, повторно объявив public state :

interface State {
  bar: number;
}

class Foo extends React.Component<{}, State> {
  public state: State;
  public myMethod() {
    this.state.bar = 5;
  }
}

Все 91 Комментарий

Какую версию машинописного текста использует ваша визуальная студия?

@vsaio для са

Для проблемы 1 с TS 2.1.5 и последней версией VSCode у меня это работает нормально. У меня нет Windows/VS, поэтому я не могу проверить там, но могу поспорить, что есть обновления для ваших плагинов, или вы не используете TS 2.1.5.

То же самое для задачи 2

VS 2015 с TS 2.1.5.0

Проблема 3 обсуждается, я думаю.

Вы правы в том, что вы можете _технически_ сделать приведенный выше пример в React, но я определенно утверждаю, что это не тот способ, которым React предназначался для использования.

Это можно разбить на 3 отдельных случая.

Общая инициализация

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  public state: State = {
    bar: 5,
  };
}

Инициализация на основе реквизита

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: props.baz,
    };

    // or
    this.setState({
      bar: props.baz,
    });
  }
}

Случайное назначение с forceUpdate

Учитывая, что я считаю, что лучше подтолкнуть людей к «правильным» вещам, вы можете легко обойти эту проблему, повторно объявив public state :

interface State {
  bar: number;
}

class Foo extends React.Component<{}, State> {
  public state: State;
  public myMethod() {
    this.state.bar = 5;
  }
}

Мои проблемы связаны с дисперсией дженериков. специально для ввода в классе, который является универсально типизированным. ниже приведен довольно минимальный пример того, где что-то ломается.

class TBaseState {
  public value: string;
}

function globalFunc<T extends Readonly<TBaseState>>(item: T) {
}

class MyComponent<TProps, TState extends TBaseState> extends React.Component<TProps, TState> {
  broken() {
    // typing of this.state is Readonly<TState>
    // this is not assignable to Readonly<TBase>
    globalFunc(this.state);

    // this is a horrible hack to fix the generics variance issue
    globalFunc(this.state as TState as Readonly<TBaseState>);
  }
}

class MyState extends TBaseState {
}

let component: MyComponent<any, MyState>;

// here the typing of component.state is Readonly<MyState>
// this is assignable to Readonly<TBase>
globalFunc(component.state);

Я в ТС 2.1.5.0

image

но может быть, что в VS у нас худший опыт работы с TS, чем в коде VS...

для проблемы 1 перейдите к определению TS также не работает в VS Code:

interface MyComponentProps {
    name: string;
}

export abstract class MyComponent extends React.Component<MyComponentProps , void> {
    fullName: string;
    myMethood() {
       this.props.name; //<-- doesnt work
       this.fullName; //<-- works
   }
}

для проблемы 2 верно, что VS Code ведет себя лучше:

image

в то время как VS выглядит сбитым с толку:

image

Я думаю, что для VSCode и проблемы 1 это работает, потому что я использую плагин для «Последней грамматики Typescript и Javascript», который должен иметь более разумную обработку.

@patsissons , это интересный пример, хотя я думаю, что он больше отражает ошибку в машинописном тексте, чем ошибку в файле определения. Например, setState раньше принимал S , что означало выполнение частичных операций, с которыми мы привыкли делать странные трюки, такие как setState({foo:5} as any as State) , или использовать тот, который принимает функцию. Я не уверен, что недостаточная выразительность компилятора делает типизацию "неправильной". Я думаю, что это достойный аргумент в пользу изменения README, чтобы отметить этот пограничный случай.

Вы подали заявку на TS?

Таким образом, это изменение в настоящее время ломает все VS и отключает Go To Definition во всех кодах VS, за исключением случаев, когда у вас есть плагин...

Также есть аргумент полноты. Существует множество API-интерфейсов, предназначенных только для чтения, а в настоящее время их нет, только в React.d.ts.

 interface ComponentLifecycle<P, S> {
        componentWillMount?(): void;
        componentDidMount?(): void;
        componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
        shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: Readonly<any>): boolean;
        componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: Readonly<any>): void;
        componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, prevContext: Readonly<any>): void;
        componentWillUnmount?(): void;
    }

Я думаю, что readonly следует использовать для «замораживания» или «Inmmutable.js», а не для длинного хвоста мыслей, которые не предназначены для изменения, например, объекты событий.

Не подавал, сегодня я просто модифицирую свой код для обработки новых типов Readonly<T> , это был случай, с которым я столкнулся, и у меня не было правильно типизированного решения. Идите вперед и отправьте сообщение о проблеме, сегодня я буду занят большую часть дня исправлением кода.

Ах да, я знал, что пропустил некоторые. @olmobrutall Если мы сохраним изменения состояния, которые я внес, чтобы пометить их как доступные только для чтения, я согласен с тем, что эти методы следует обновить. Я чувствую, что нам нужен консенсус в отношении того, что нужно делать в первую очередь.

Что касается разрывов VS, я не знаю, что правильно. Следует ли сдерживать типы, потому что некоторые инструменты не обновляются?

@patsissons Вы всегда можете предоставить свои собственные типы для реагирования на данный момент, если хотите подождать, чтобы увидеть, как это сработает, прежде чем обновлять весь свой код. https://ericlanderson.com/using-custom-typescript-definitions-with-ts-2-x-3121db84015d#.ftlkojwnb

по нашему опыту, VS всегда немного отстает. Наш магазин использует vscode для любой активной разработки машинописного текста, а VS используется больше для простого исправления файлов кода или для разработчиков, не использующих машинописный текст, для просмотра кода, не обязательно для активной разработки.

@ericanderson взлом не так уж и плох на данный момент, мне просто нужно отсеять Readonly<T> , чтобы получить мой T , который можно присвоить Readonly<Base> .

Мы говорим о 'react.d.ts', это одночленное объявление широко используется. Думаю, стоит повременить, пока VS не будет готов.

Кроме того, примерно 50% типов в мире предназначены только для чтения, например, объекты, которые вы получаете из API, я не думаю, что нам нужно аннотировать это.

Я думаю, что Readonly следует использовать для объектов, которые были явно преобразованы, чтобы иметь свойства только для чтения. Как заморозить.

@olmobrutall Readonly является новым, поэтому точная передовая практика на самом деле не определена. Лично я бы предпочел, чтобы все заявляло, что требуется Readonly<> вещей, чтобы обозначить, что он не будет его видоизменять. Точно так же React не ожидает, что вы измените state за пределами setState , и, таким образом, это изменение гарантирует, что несчастные случаи не приведут к ошибкам, что является одним из основных преимуществ использования TypeScript по сравнению с javascript.

Если бы производительность была более стабильной в разных браузерах за Object.freeze , я бы предположил, что ребята из React действительно начали бы зависать после setState .

Какова цель forceUpdate тогда?

Мне любопытны мысли других людей о том, как DefinitelyTyped должен работать в отношении обновлений инструментов, а также философия о Readonly при реагировании (и других библиотеках, целью которых является то, что вы не изменяете определенные объекты).

cc/ @johnnyreilly @vsaio @pspeter3 для мыслей о реакции конкретно и других мыслей в целом
cc/ @andy-ms @mhegazy за мысли о том, как DefinitelyTyped следует философски относиться к обновлениям инструментов и ревностному использованию Readonly

@olmobrutall мы используем forceUpdate для постановки в очередь рендеринга на стороне реагирования, управляемого наблюдаемыми событиями на стороне состояния.

ОБНОВЛЕНИЕ :
Я немного поясню наш сценарий, чтобы его не поняли неправильно. Наши объекты состояния являются долгоживущими неизменяемыми объектами (поэтому Readonly<T> на самом деле очень подходит для нас). Эти объекты состояния содержат несколько наблюдаемых потоков rxjs , которые направляются в наблюдаемый объект уведомления с именем stateChanged . Компоненты React наблюдают за этим наблюдаемым событием и направляют эти события в вызов forceUpdate (после устранения дребезга). По сути, наше изменяемое состояние живет внутри состояния, но само состояние и члены, существующие в состоянии, неизменны. Это, конечно, не стандартный вариант использования React, но он очень похож по ходу. У нас просто есть объекты умного состояния, которые знают, как информировать компоненты, когда требуется повторный рендеринг.

@ericanderson , основная проблема в том, что эти определения типов страдают от проблем с SemVer. Поскольку версии определения типа в значительной степени привязаны к своим соответствующим версиям модуля, мы в конечном итоге получаем незначительный скачок версии, который приводит к критическим изменениям определения типа, а это означает, что мы должны закрепить версии @types в нашем package.json файл.

@olmobrutall Из документов реакции:

Обычно вы должны стараться избегать любого использования forceUpdate() и читать только из this.props и this.state в render().

Руководство по реагированию на самом деле говорит вам не обновлять состояние напрямую: https://facebook.github.io/react/docs/state-and-lifecycle.html#do -not-modify-state-directly.

forceUpdate , насколько я читал, предназначен только для принуждения вашего компонента к обновлению, когда ваш компонент основан на данных _не_ в ваших реквизитах или вашем состоянии.

@patsissons Я могу ошибаться, но я считаю, что SemVer разработан для обратной совместимости с API и семантическими намерениями. Тот факт, что вы используете библиотеку не по назначению (в соответствии с документацией), не означает, что библиотека должна поддерживать указанное непреднамеренное использование. Авторы библиотеки хорошо разбираются в SemVer за изменение семантики, которая была неправильной, но использовалась некоторыми людьми.

Тем не менее, возможно, изменение с Readonly<> на state слишком большое, но предположим на мгновение, что это правильное изменение. Когда он должен быть выпущен в DefinitelyTyped? Ваш код всегда будет нуждаться в изменении, как только вы получите обновление, которое, наконец, пометит state как Readonly<> .

Я до сих пор не знаю, что правильно в том, что Readonly<> применяется к state , что затрудняет обсуждение semver, инструментов или чего-либо еще. Моя интуиция была в том, что это было правильно. Люди, которые рассмотрели изменение, никогда не поднимали его как проблему. Кажется, это соответствует намерениям команды React.

Я рад обратиться к любому из рецензентов за реакцию в DefinitelyTyped (я скопировал их всех выше).

Итак, Observable изменяет состояние, когда вы принудительно обновляете? Таким образом, императивное изменение состояния несколько разрешено.

Я понимаю, что место, где следует использовать Readonly, не определено на 100%. Но нужно ли нам начинать со спорного и широко используемого свойства до того, как инструменты будут готовы?

Я полностью за строгую типизацию, я поддерживаю 6 проектов со strictNullChecks, но преимущества здесь намного меньше, чем проблемы, которые он создает в настоящее время.

@ericanderson Я полагаю, что SemVer2 разработан так, чтобы разрешать объявления версий узлов, такие как ^15.0.0 , и ожидать, что любые незначительные обновления или обновления исправлений (например, 15.0.1 или 15.1.0 ) модуля будут прозрачными или по крайней мере обратно совместим с внешней точки зрения. Любые крупные обновления ( 16.0.0 ) потребуют внесения изменений в объявление версии. По сути, это предотвращает внесение критических изменений в систему типизации. Но в настоящее время версии определения типа не могут отличаться от основной версии соответствующей версии модуля (по соглашению), что приводит к этому разрыву.

Краткая версия заключается в том, что в определения типов могут быть внесены критические изменения без изменения самого модуля, а критические изменения требуют увеличения основной версии.

А вот пиар с удалением forceUpdate не сделаете, не так ли?

Тогда, если есть forceUpdate, императивное изменение состояния тоже должно быть.

Теоретически вы должны использовать глубокий неизменяемый граф состояний, но очень часто императивное изменение состояния просто прекрасно, и не все разработчики верят в идею персистентных структур данных.

К счастью, React позволяет обходной путь и делает возможным прямое изменение состояния, собираемся ли мы запретить разработчикам TS идти по этому пути? Не слишком ли это по-патерналистски?

Vue.js, например, продвигает императивные изменения, я не удивлюсь, если это повлияет на React.

Также я недавно читал сообщение в блоге какого-то автора React (я могу припомнить), поощряющее использование React без всей церемонии Redux.

В противном случае моя позиция состоит в том, чтобы опубликовать определения типов как можно скорее, моя единственная забота — автоматизация. поскольку, учитывая текущее состояние версии определения типа, последующая сборка без изменений исходного кода может сломаться. Эти типы недетерминированных сбоев CI вызывают беспокойство. Никто не хочет, чтобы их сборка сломалась, потому что они протолкнули изменение, а затем узнают, что их рука изменения не имеет ничего общего с нарушением сборки.

После сна на нем мое личное мнение таково:

  • Хорошей практикой будет использование блокировок пряжи или npm, поэтому вы не получите сюрприз, если сначала не обновитесь локально.
  • Создание состояния только для чтения — это то, как React предназначен для использования. Документация и примеры подтверждают это.
  • Рабочий процесс, в котором вы не хотите использовать setState, находится в пределах вашей кодовой базы и в пределах ваших собственных прав. Вы правы в том, что React предоставляет forceUpdate, но его использование предназначено для вызова рендеринга, когда вы находитесь за пределами предполагаемого варианта использования. Таким образом, если вы не хотите использовать состояние по назначению, это нормально, но в этот момент вам не нужно использовать состояние переменной экземпляра. На самом деле вы можете просто использовать обычные частные переменные.
  • Да, от этого проекта зависит множество людей, и пока что это единственные две жалобы, которые заставляют меня думать, что это не широко распространенная проблема. Кроме того, проблема, связанная с глобальной функцией, может быть просто переписана, чтобы по-другому использовать универсальную (см. связанную проблему TypeScript)

Учитывая приведенные выше мысли, а также обходные пути для нестандартных приложений React, я думаю, что Readonly является правильным, и единственное необходимое изменение для полноты — это обновить методы жизненного цикла, чтобы они соответствовали.

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

Патсиссон, используя VS, VS Code или любой другой редактор?

Я хочу сказать, что, хотя React продвигает функциональный подход, он позволяет использовать рабочий процесс, похожий на видеоигру, когда вы императивно вносите изменения в мир (состояние), а затем выполняете рендеринг (forceUpdate). Этот подход теперь запрещен в TS. Этакий функ-даментализм :)

Тогда я подумаю об альтернативах, чтобы сделать мою текущую экосистему работоспособной...

Проблема 2 выдает ошибку только с strictNullChecks .

[TS] Cannot invoke an expression whose type lacks a call signature. Type '((val: any) => void) | undefined' has no compatible call signatures.

+1 Я использую strictNullChecks

@Эрикандерсон ?

Как указано выше, это связано с инструментальной оснасткой, которая явно выходит за рамки DT. Если вы пользуетесь VSCode и устанавливаете предварительную версию грядущего синтаксического анализатора TS, я не видел этой проблемы со strictNullChecks, когда писал что-либо из вышеперечисленного.

У меня нет окон, поэтому я не могу нормально разговаривать с VS.

этот патч Pick сломал предложения в VSCode. при выполнении this.setState({ | }) (Ctrl + пробел) ничего не отображается, хотя состояние четко определено и используется Partial<State> , поскольку setState может выборочно устанавливать состояние членов

ИМХО правильный код должен быть setState(state: Partial<S>, callback?: () => any): void;

Как обсуждалось в исходной ветке запроса на вытягивание, мы начали с Partial. Однако, если ваш объект состояния:

состояние интерфейса {
фу: строка;
}

Затем с частичным вы можете сделать следующее:

setState({foo: не определено});

Что явно неправильно.

извините - но опять про Readonly

React — это не только Redux и setState. React также является mobx и другими наблюдаемыми паттернами, где назначение свойств состояния является ГЛАВНОЙ ФУНКЦИЕЙ . Обсуждаемое изменение полностью убивает использование mobx с typescript.

Так зачем же добавлять в файл .d.ts поведение, которого нет в исходном коде реакции? .d.ts должен быть отражением оригинальной библиотеки, а не обучением людей правильному стилю кодирования, по мнению автора!

@lezious Боюсь, я не понимаю твоей позиции. Можете ли вы предоставить образец кода и что не так с типизацией по отношению к образцу? Спасибо!

Без проблем:

это мой государственный класс

class UserInfoBlockState  
{
    <strong i="7">@observable</strong>                  <- this is mobx way to declare state
    public updating: boolean;
    <strong i="8">@observable</strong> 
    public deleted: boolean;
}

и это мой компонент

<strong i="12">@observer</strong>       <-- this is mobx way to make component react to state change
export class UserPanel extends React.Component<IUserInfoBlockProps, UserInfoBlockState>
{
   ......
     private updateUser()
    {
        this.state.updating = true;
        UsersAPI.update(this.props.user)
       .then(() =>
            {
                this.state.updating = false;      <--- this is the mobx way to work with the state
            }
        ).catch(() =>
            {
                this.showErrror("Server error");
                this.state.updating = false;
            });
    }
   ....

}

и теперь мы (наша компания с огромным проектом, написанным на react+mobx) обновили DT и React при запуске нового круга релизов и... 3000+ ошибок компиляции "property is readonly". Вот это да. Что вы предлагаете мне сделать - переписать весь проект на редукс, никогда не обновлять react.d.ts или всегда сохранять и поддерживать разветвленную версию?

@mweststrate , пожалуйста, проверьте это.

@Iezious Я ценю твою позицию и прошу тебя успокоиться. Я пытаюсь работать с вами. Это не имеет ничего общего с Redux, а чистый React.

Я не хочу, чтобы DT блокировал вариант использования, который работал ранее, однако я не думаю, что то, как вы описываете использование mobx с реакцией, согласуется с документацией по реакции (и даже с документацией по mobx теперь, когда я прочитал на Это).

В документации React четко указано, что существует 3 способа правильного использования состояния, и самый первый из них — «Не изменять состояние напрямую».

Это наводит меня на мысль, что то, как сейчас работает ваша кодовая база, вполне вероятно, сломается в будущих версиях реакции. Просматривая https://github.com/mobxjs/mobx-react , я не вижу никаких предположений, что вы используете состояние таким образом. На самом деле, похоже, они хотят, чтобы вы использовали свойства.

Просмотрев https://mobx.js.org/getting-started.html и погуглив «состояние реакции mobx», я не нашел никакой документации, в которой предлагалось бы использовать mobx так, как вы это делаете.

Предполагается, что DT в лучшем случае передает дух базовой библиотеки, а в худшем — реальную реализацию, и ясно, что покупка реактивного и расширяющего компонента означает соблюдение подразумеваемого контракта.

Я не уверен, что я предлагаю вам сделать. Несколько вариантов, которые я могу придумать навскидку:

  1. «Дешевый» вариант, если вы настаиваете на использовании переменной state , заключается в поиске и замене React.Component на MyComponent и определении MyComponent в качестве подкласса. из React.Component без ограничений только для чтения.
  2. Другой, основанный на идиоматических примерах, опубликованных в документации для mobx, заключается в том, чтобы не торопиться с использованием this.state и просто использовать переменные в фактическом React.Component . Это может быть немного болезненно, но, по крайней мере, новые люди в вашем проекте смогут увидеть шаблоны в вашей кодовой базе, как они описаны в Интернете.
  3. Вы можете повторно объявить state в каждом компоненте, если хотите продолжать делать это так же, как вы делаете.
  4. Вы можете найти и заменить this.state на this.somethingElse и объявить вручную.
  5. Вы можете перестать получать обновления для реагирования на DT (и, возможно, в будущем для реагирования в целом, в зависимости от того, как будущие изменения могут повлиять на ваш вариант использования).

Если бы это был мой проект, я бы, вероятно, сделал номер 2, хотя я недостаточно знаю о mobx, чтобы знать наверняка.

Извините, я не мог согласиться с вами (это не значит, что другие не согласятся с вами). Я пытался найти причину, чтобы вернуть эту часть, но сейчас я не могу понять.

Я еще раз упомяну нашу стратегию, которая представляет собой специальное приложение наблюдаемых объектов RxJ для управления изменениями состояния и рендеринга React, что очень похоже на шаблон mobx. Мы используем действия для получения входных данных от слоя представления (React). Действия являются синонимами функции, которая потребляет входные данные и создает наблюдаемое, которое затем будет в дальнейшем управлять другими наблюдаемыми состояниями. Этот шаблон позволяет состоянию оставаться неизменным с точки зрения уровня React, поскольку вы всегда запрашиваете только наблюдаемые значения и выполняете действия состояния. Внутренне ваше состояние может «мутировать» в результате действий, поскольку внутреннее состояние не имеет ограничений только для чтения.

Я не могу успокоиться. У меня есть проект в производстве, и мне нужно потратить огромное количество времени команд, а это означает деньги после этого изменения.

И в чем причина? Отражает ли это изменение реальность в реакции? Нет. Он добавляет ограничения и поведение, которого нет в React, ограничение, которое добавляется каким-то образом только потому, что он считает, что это правильно.

Какова цель проекта DT? Максимально точно описать JS-библиотеки или описать наше видение способа правильного использования этих библиотек? Судя по названию "Definitely Typed" он первый. Таким образом, если этого ограничения нет в исходной библиотеке JS, оно НЕ ДОЛЖНО существовать и в DT. Это моя точка зрения. Где я не прав в этом пункте?

Я понимаю, что вы расстроены, однако именно так реакция должна была использоваться при чтении документации. Я не понимаю, как нам сделать DT менее конкретным, потому что ваша команда злоупотребила библиотекой и нарушила подразумеваемый контракт.

Покажите мне одну унцию документации, выпущенной командой реагирования, которая предполагает, что состояние должно быть напрямую изменяемым, и я немедленно верну код обратно.

https://facebook.github.io/react/docs/react-component.html#state

Никогда не изменяйте this.state напрямую, так как последующий вызов setState() может заменить сделанную вами мутацию. Относитесь к this.state так, как если бы оно было неизменным.

Кажется довольно ясным, что реакция считает this.state неизменным. React не считает _properties_ this.state неизменяемыми (что является избыточным предположением). Вы можете делать:

this.state.user.name = "foo";

в идиоматическом реагировать.

Я предпочитаю точно печатать API (что в данном случае означает Readonly ) и выражать все инварианты, которые заявляет команда реагирования.

@ericanderson извините, я только что заметил это. FWIW Я думаю, что это изменение разумно, и этот инструментарий наверстает упущенное. Кстати, вы слышали, что они рассматривают возможность отказа от перегрузки setState , которая принимает объект? Будущее за стилем сокращения setState по всем параметрам.

@amoreland Не согласен. Пер: https://facebook.github.io/react/docs/state-and-lifecycle.html#do -not-modify-state-directly

Не изменять состояние напрямую

Например, это не приведет к повторному рендерингу компонента:

// Wrong
this.state.comment = 'Hello';

Вместо этого используйте setState():

// Correct
this.setState({comment: 'Hello'});

Единственное место, где вы можете назначить this.state, — это конструктор.

@johnnyreilly У меня не было. Это интересно. Источник?

Об этом говорилось в одном из выступлений на недавней реакционной конференции. Он доступен на YouTube. Возможно, это была речь Лин Кларк. Одно из первых выступлений в первый день — о предстоящих изменениях, связанных с реакцией на v16. Волокно и т. Д.

Извини , @gaearon, я имел в виду

Причина, по которой mobx выполняет изменения состояния, заключается в том, что наблюдаемые объекты управляют обновлениями React, вместо того, чтобы полностью заменять состояние, состояние становится механизмом, управляющим рендерингом. поэтому, делая что-то вроде this.state.updating = true; , вы фактически выполняете эквивалент замены состояния, но вместо этого состояние достаточно умно, чтобы запускать новый рендеринг, уведомляя компонент о том, что состояние изменено по сравнению с его предыдущим содержимым. Я согласен, что это не _традиционное_ использование React, а гораздо более полное и более высокое использование React. Я бы сказал, что обычное использование React полезно только для небольших проектов с небольшим количеством компонентов. Когда вы закончите с сотнями компонентов, каждый с несколькими десятками реактивных драйверов мутации, вы больше не сможете надежно использовать обычные изменения состояния React (например, setState) и должны принимать архитектурные изменения, которые включают _smart_ состояние (что, по сути, делает mobx). ).

Таким образом, я понимаю, почему он расстроен, потому что изменения набора текста влияют на более продвинутое использование системы React. Назначения наблюдаемого состояния технически не являются _мутирующим_ состоянием, а вызывают наблюдаемые события для значения RHS. Так уж получилось, что синтаксис, который mobx выбрал для выдачи этих наблюдаемых событий, конфликтует с явным намерением неизменяемого состояния React.

@Iezious , если вам нужно быстрое решение проблемы такого типа, вы можете избежать этого, увеличив типизацию React и реорганизовав свои компоненты, чтобы использовать расширение для определений типа React.Component .

import * as React from 'react';
declare module 'react' {
  class MutableStateComponent<P, S> extends React.Component<P, S> {
    state: S;
  }
}
(React as any).MutableStateComponent = React.Component;

и теперь вы можете просто создавать компоненты с изменяемым состоянием, как и обычные компоненты, за исключением того, что их элемент state больше не помечен как readonly .

export class MyComponent extends React.MutableStateComponent<MyProps, MyState> {
  // this.state.name is writable
}

@patsissons да, именно в этом причина.

Я не мутирую состояние, я использую mobx observables, который вызывает для меня setState, я делаю это, так как код моего ОГРОМНОГО проекта выглядит намного яснее и понятнее.

Я знаю, что могу сделать свой компонент, я также могу сделать скрипт на своем сервере npm, который всегда будет отменять это изменение для нашей компании. Я могу использовать такой хак, как «this.state.state.updated» и множество других хаков.

Я просто хочу сказать, что это изменение влияет на использование наблюдаемых шаблонов, таких как mobx, которые на самом деле следуют способу реагирования, но теперь, поскольку это изменение не может быть скомпилировано и для его работы требуются некоторые хаки и обходные пути. И именно поэтому я думаю, что это изменение неправильно.

возможно, вместо предложенного мной MutableStateComponent мы назовем его ObservableComponent , что больше соответствует шаблону Observable React.

Если вы отбросите Readonly , то люди, которые используют типы реакции с обычной реакцией (и/или любое количество других систем управления состоянием, кроме mobx), будут подвержены ошибкам. Я не использую mobx в своем огромном проекте, и я ценю ошибки компилятора, когда кто-то делает опечатку и случайно использует this.state.foo = bar .

Если существует неизбежный компромисс между использованием стандартной и нестандартной реакции, стандартные типы реакции должны склоняться к первому.

Кроме того, если вы используете mobx идиоматически, это не проблема.

Если вы откажетесь от Readonly, то люди, использующие типы реагирования с обычными реакциями (и/или любое количество других систем управления состоянием, помимо mobx), будут подвержены ошибкам. Я не использую mobx в своем огромном проекте, и я ценю ошибки компилятора, когда кто-то делает опечатку и случайно использует this.state.foo = bar.

Так что еще раз - вы УЧИТЕ ПИСАТЬ КОД

Этот проект не для обучения написанию кода, этот проект для описания существующего функционала JS библиотек. Обсуждаемое ограничение отсутствует в исходной библиотеке и должно быть удалено.

Это все.

@patsissons

если вам нужно быстрое решение проблемы такого типа, вы можете избежать этого, дополнив типизацию React и реорганизовав свои компоненты, чтобы использовать расширение для определений типа React.Component.

это не правильно. В корпоративном мире, откуда я работаю, не существует «быстрых решений». Когда что-то меняется в SDK, оно либо ДОЛЖНО быть обратно совместимым, либо на это уходят годы. В проекте из 2 миллионов строк кода нет быстрых исправлений. Это недели изменений, тестирования, A/B-тестирования, развертывания для большого количества людей. Это стоит реальных денег. И все эти огромные усилия только из-за того, что кто-то добавил обратно несовместимое изменение, которого НЕ СУЩЕСТВУЕТ В РЕАЛЬНОЙ БИБЛИОТЕКЕ?

Как вы думаете, почему forceUpdate все еще существует в реакции? Они обсуждают в конфах правильный стиль, но вносят изменения, чтобы предотвратить другие способы использования. Почему? Поскольку это большая компания, и они знают, что библиотеки должны быть обратно совместимыми.

@ericanderson написал, что

this.state.state.value = 1 

тоже не законно с его точки зрения. В следующий раз он получит инструмент от TS и добавит ограничение, которое предотвратит этот код. Либо сделать компонент финальным классом, либо что-то еще, просто потому что "это правильный стиль и не дает людям делать ошибки".

Предотвратить митаки людей - это задача FB, если они хотят это сделать, они могут легко добавить прокси и запретить изменение состояния. Цель DT — добавить описания, и больше ничего.

@Iezious

Я думаю, дело в том, что команда React не может сделать состояние неизменным с помощью JavaScript, но если бы они могли, они бы это сделали. С другой стороны, TypeScript может сделать состояние неизменным, поэтому это изменение было внесено в определения типов. Цель команды React заключалась в том, чтобы запретить изменение членов состояния напрямую (о чем свидетельствует их официальная документация по правильному использованию состояния ), но у них не было языковых конструкций, чтобы наложить это ограничение. Это ограничение никогда не было неизвестным, оно было хорошо задокументировано с самого начала. Для тех из нас, кто работает за пределами _традиционного_ использования React, мы должны, по крайней мере, придерживаться официальных рекомендаций по использованию React. Для моей команды это означало разработку решения, позволяющего управлять изменениями состояния без непосредственного изменения состояния. Это было сделано специально, чтобы гарантировать, что мы не столкнемся с какими-либо проблемами, подобными этой (хотя это изменение немного коснулось нас, но только через сигнатуры функций, а не фундаментальный дизайн).

Если в вашей ситуации рефакторинг невозможен, то либо закрепите @types/react на 15.0.1 до внесения изменений, либо просто не используйте @types/react и вместо этого поддерживайте свой собственный вариант введите defs и просто измените ввод state на Component . Я действительно не думаю, что вам удастся убедить кого-либо отменить изменение.

forceUpdate существует, потому что он задокументирован как рекомендуемый путь кода для управления отрисовкой при изменении внутреннего структурного состояния (или когда render() использует данные вне изменяемого состояния). Использование forceUpdate предназначено именно для шаблона использования, который моя команда использует с React.

Команда React МОЖЕТ сделать состояние неизменным с помощью JS, это легко. Но без обратной совместимости.

Еще раз, это:

this.state.state.value = 1 

законно или нет?

Я думаю, что да, но мое мнение уже ясно...

Я не думаю, что разговор об изменчивости/неизменяемости актуален _пока_. Очевидно, что ошибка в компиляторе TS (относительно Readonly ) в сочетании с этим изменением делает использование универсальных компонентов совершенно невозможным, нарушая большую часть существующего кода. Конечно, это общепринятый допустимый вариант использования, и это достаточная причина, чтобы откатить его на данный момент ???

interface test1 {
    num: number;
}

function add<T extends test1>(initial: T, add: number) {
    var copy: Readonly<T> = initial;

    //ERROR HERE: [ts] Operator '+' cannot be applied to types 'T["num"]' and 'number'.
    return copy.num + add;
}

Кто-нибудь знает, есть ли открытая проблема с командой Typescript по этому поводу? Я не могу найти соответствующую проблему на их трекере.

@caesay См. Microsoft/TypeScript#15501

мне нужно инициализировать состояние в конструкторе, но tslint показывает «состояние только для чтения»....

constructor(props) {
  super(props);
  this.state = {
     value: props.defaultValue,
  };
}

как исправить..............

Используйте setState

setState не работает в конструкторе

Или рассмотрите componentWillMount с setState

Спасибо

Привет,

Я прочитал всю ветку, но мне не ясно, есть ли планы по обработке сценария @alanwei0 ?

Я полностью согласен с тем, что имеет смысл иметь state как ReadOnly . При этом невозможность установить начальное состояние в конструкторе немного усложняет ситуацию, потому что render вызывается до того, как будет выполнено componentDidMount .

@pawelpabich, использующий this.state = { в конструкторе, не проблема. Вы можете присваивать переменным readonly в конструкторе, а Readonly<T> не препятствует присваиванию (никогда!).

interface TInterface {
    test: string;
}

class TClass {
    readonly blah: Readonly<TInterface>;
    constructor() {
        this.blah = { test: "constructor" };
    }

    fn = () => {
        this.blah = { test: "fn" };
    }
}

Единственная ошибка здесь будет внутри fn — не из-за Readonly<T> , а из-за ключевого слова readonly . На самом деле, последняя версия типизации даже не использует ключевое слово readonly , так что вы можете назначать состояние где угодно, просто не изменяя свойства внутри состояния.

Проблема, о которой мы здесь говорили, была ошибкой компилятора машинописного текста, из-за которой свойства состояния теряли свои типы в унаследованных компонентах. С тех пор это было исправлено, я думаю, и если это так, этот вопрос может быть закрыт.

@caesay извините, я думал, что мы говорим об этом. Моя проблема в том, что я не могу установить состояние в базовом классе. У меня ТС 2.4.1. У вас случайно нет идентификатора проблемы, чтобы я мог это проверить?

Вы всегда можете вызвать setState (в componentWillMount).

@pawelpabich Опять же, это не проблема :) вы не можете специально назначить состояние из базового класса. Как ты мог? вы не знаете контракт prop, используемый производным компонентом.

interface BaseCompState {
    baseState1?: string;
}

class BaseComp<TState extends BaseCompState> extends React.Component<any, TState> {
    constructor(props) {
        super(props);
        this.state = {
            baseState1: "fromBase",
        };
    }
}

interface TopCompState extends BaseCompState {
    topState1?: string;
}

class TopComp extends BaseComp<TopCompState> {
    constructor(props) {
        super(props);
        this.state = {
            baseState1: "fromTop",
            topState1: "fromTop",
        };
    }
}

Это простой пример производного компонента (реквизиты опущены, но идея та же). this.state = в базовом классе, очевидно, не может работать, потому что он не знает, что такое TState . кроме того, если бы он _сработал_, то он бы просто перезаписал состояние, установленное родителем. Конечное состояние будет { baseState1: "fronBase" } . Что случилось со свойством topState?

Если вы абсолютно уверены, что вам нужно обрабатывать состояние в базовом компоненте, вы можете передать состояние из производного компонента в конструктор базового компонента (как TState , чтобы вы могли его назначить) — это может выглядеть примерно так: это:

interface BaseCompState {
    baseState1?: string;
}

class BaseComp<TState extends BaseCompState> extends React.Component<any, TState> {
    constructor(props, state: TState) {
        super(props);
        this.state = Object.assign({
            baseState1: "fromTop",
        }, state);
    }
}

interface TopCompState extends BaseCompState {
    topState1?: string;
}

class TopComp extends BaseComp<TopCompState> {
    constructor(props) {
        super(props, {
            topState1: "fromTop",
        });
    }
}

Или, что еще проще, вы можете вызвать this.setState( из вашего базового компонента (да, вы можете сделать это в конструкторе!)

Здесь нет проблем.

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

import * as React from "react";

/* tslint:disable:max-classes-per-file*/

interface BaseProps {
    baseProp: string;
}

interface BaseState {
    baseState: string;
}

class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
    constructor(props) {
        super(props);

        this.state = {
            baseState: props.baseProp
        };
    }

    render() {
        return <div>{this.state.baseState}</div>;
    }
}

interface DerivedProps extends BaseProps {
    derivedProp: string;
}

interface DerivedState extends BaseState {
    derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
    constructor(props) {
        super(props);

        this.state = {
            derivedState: props.derivedProp
        };
    }

    render() {
        return <div>{this.state.derivedState}</div>;
    }
}

Ошибки

webpack: Compiled successfully.
ERROR at Test.tsx(17,9):
TS2322: Type '{ baseState: any; }' is not assignable to type 'Readonly<TState>'.

ERROR at Test.tsx(39,9):
TS2322: Type '{ derivedState: any; }' is not assignable to type 'Readonly<DerivedState>'.
  Property 'baseState' is missing in type '{ derivedState: any; }'.

Version: typescript 2.4.1

Первый. Ваш реквизит в базе в конструкторе не набирается. Таким образом, props.baseProp равно any , что не может быть присвоено!

Во-вторых, ваши реквизиты в Derived имеют ту же проблему, И вам не хватает baseState . Который конечно не будет работать вне зависимости от Readonly

TProps extends BaseProps , что означает, что TProps состоит как минимум из тех же участников, что и BaseProps . Так как же это не определено? Я понимаю, что компилятор не сможет это сделать, но сказать, что это не определено, кажется неправильным. То же самое можно применить и к Derived .

@caesay setState в конструкторе не является надежным решением, потому что этот метод является асинхронным, и вы все равно можете перейти к render без установки начального состояния.

Единственное надежное решение, которое я вижу, - это установить все состояние в производных классах. У этого есть очевидный недостаток, который:

  1. Это должно быть продублировано в каждом производном классе.
  2. Производные классы должны знать о состоянии, которое их не волнует.

Пример, который я показал выше, будет работать на C#, поэтому было бы неплохо, если бы он мог работать и на TypeScript.

  1. ~setState безопасен в конструкторе~
  2. В случае производного класса лучшим вариантом, который я вижу, является вызов setState в вашем componentWillMount . Ваша база не знает, какое состояние должно быть у ее дочернего элемента, поэтому она не может перевести this.state в безопасную конфигурацию. Однако ваш подкласс может вызвать componentWillMount родителя, а затем установить любое состояние, которое, по его мнению, ему также нужно.
  3. Во многих языках есть языковые функции, которые было бы неплохо иметь в машинописном тексте. Некоторые осуществимы. Другие нет. В любом случае это не аргумент для их включения.
  4. Ошибки, которые вы видите, имеют смысл. Они не указывают на ошибку ни в машинописном тексте, ни в наборе текста. В каждом случае вы буквально пытаетесь присвоить this.state объекту, который не соответствует ожидаемому типу.

EDITED, чтобы отразить, что я был неправ относительно setState в конструкторе, и добавить обратные кавычки для удобства чтения.

@ericanderson Я не думаю, что мы добились здесь какого-либо прогресса. Я показал вам пример, и я был бы признателен, если бы вы могли сосредоточиться на нем. В противном случае трудно вести дискуссию.

Re setState небезопасно использовать в конструкторе. Он не выдает ошибку, но не устанавливает состояние до того, как произойдет рендеринг. У меня есть код, который ломается из-за этого, и документы React очень ясны, что их не следует использовать там.

@pawelpabich Нет, это не место для этого аргумента. Вы в корне не правы в своем понимании языка. В приведенном вами примере вы не выполнили «Государственный» контракт ни в ОДНОМ из ваших назначений государству.

Например, когда вы делаете

this.state = { baseState: props.baseProp };
// now the state is exactly { baseState: props.baseProp }

Состояние равно { baseState: props.baseProp } , и это НЕ удовлетворяет требованию TProps extends BaseProps (потому что мы не знаем, что такое TProps! У него могут быть какие-то свойства).

После этого вы выполняете _ ОТДЕЛЬНОЕ _ задание.

this.state = { derivedState: props.derivedProp };
// now the state is exactly {  derivedState: props.derivedProp }

состояние теперь ровно { derivedState: props.derivedProp } (вы перезаписали назначение базового состояния!!), и это не удовлетворяет DerivedState ИЛИ BaseProps.

Вы совершенно заблуждаетесь, думая, что это должно работать. Если у вас есть проблемы с тем, как работает базовое назначение переменных, обсудите это с разработчиками языка в новом выпуске и получите ответ там, чтобы мы перестали получать уведомления здесь, пожалуйста.

В качестве примечания: вы ТАКЖЕ переопределяете свой базовый метод render() , что означает, что ваш базовый компонент не сможет ничего отображать. Если вы уверены, что это именно то, что вам нужно, вы можете добавить защищенный член getBaseState() и вызвать его из производного конструктора при установке состояния (так что вам не нужно дублировать логику базового состояния). Но я думаю, что вы действительно хотите вообще не использовать производные компоненты. Попробуйте реструктурировать, чтобы использовать композицию (где у вас есть один объект, содержащий несколько дочерних объектов). Я думаю, вы обнаружите, что это окажется намного чище.

Едва ли мне хочется оторваться от чтения, чтобы создать новый проект только для того, чтобы обсудить это с вами, но увы...

Я буду исправлен насчет setState () в конструкторе, но это не меняет моего отношения к его использованию в componentWillMount .

Рабочий пример того, как это будет сделано:
https://github.com/ericanderson/set-state-пример

В частности, index.tsx:

import * as React from "react";
import * as ReactDOM from "react-dom";

/* tslint:disable:max-classes-per-file*/

interface BaseProps {
  baseProp: string;
}

interface BaseState {
  baseState: string;
}

class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
  public componentWillMount() {
    this.setState({
      baseState: this.props.baseProp,
    });
  }

  public render() {
    return (
      <p>
        <code>this.state.baseState: </code>
        {this.state.baseState}
      </p>
    );
  }
}

interface DerivedProps extends BaseProps {
  derivedProp: string;
}

interface DerivedState extends BaseState {
  derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
  public componentWillMount() {
    super.componentWillMount();
    this.setState({
      derivedState: this.props.derivedProp,
    });
  }

  public render() {
    return (
      <div>
        <p>
          <code>this.state.derivedState: </code>
          {this.state.derivedState}
        </p>
        {super.render()}
      </div>
    );
  }
}

ReactDOM.render(<Derived derivedProp="its derived" baseProp="its basic" />, document.getElementById("main"));

@pawelpabich , если вы хотите реализовать полиморфный компонент с начальным состоянием, вам нужно будет сделать базовый компонент абстрактным и создать абстрактную функцию getInitialState() (или аналогичную) для реализации в производном классе. Вы хотите назначить состояние только один раз, либо в конструкторе, либо с помощью setState , как показал @ericanderson .

ниже приведен ваш пример, преобразованный в полностью полиморфное решение с полным разделением задач в отношении построения состояния:

interface BaseProps {
  baseProp: string;
}

interface BaseState {
  baseState: string;
}

abstract class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
  constructor(props: TProps) {
      super(props);

      this.state = this.getInitialState();
  }

  protected abstract getInitialState(): TState;

  protected getBaseState() {
    return this.props.baseProp;
  }

  render() {
      return <div>{this.state.baseState}</div>;
  }
}

interface DerivedProps extends BaseProps {
  derivedProp: string;
}

interface DerivedState extends BaseState {
  derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
  getInitialState(): DerivedState {
    return {
      baseState: this.getBaseState(),
      derivedState: this.props.derivedProp,
    };
  }

  render() {
      return <div>{this.state.derivedState}</div>;
  }
}

@patsissons спасибо!

@caesay Признаю , что был не прав и почему-то не увидел, что задания переопределяют друг друга. При этом использование CAPS и ! не помогло мне выбраться из моей дыры.

@patsissons и @ericanderson сосредоточились на проблеме, и теперь у нас есть решение, которое могут использовать другие.

@pawelpabich Я согласен, что мои манеры были менее чем профессиональными, но это понятно, учитывая, что я дал вам несколько объяснений, примеров и т. д., а вы решили не слушать меня.

тогда это будет просто перезапись состояния, установленного родителем.

[_если вы хотите_] обрабатывать состояние вашего базового компонента, вы можете передать состояние из производного компонента в конструктор базового компонента

[_если вы хотите обрабатывать состояние в производном компоненте_] вы можете добавить защищенный член getBaseState() и вызывать его из производного конструктора при установке состояния.

Что сделал @patsissons , так это взял комментарии, уже упомянутые здесь, и предоставил пример кода, который не должен был быть необходим. Это не stackoverflow, и мы не часто предоставляем там готовые примеры кода.

Я новичок в реагировании и машинописи, возможно, я чего-то не знаю, но даже если приложение компилируется без ошибок, предупреждений и подсказок, я получаю ошибку времени выполнения. Ниже приведен пример компонента. Я приписываю ошибку состоянию Readonly . Если приложение работало до изменения Readonly , то после этого изменения оно перестало работать и не выдает ошибок времени компиляции.

import * as React from 'react';

export default class HomePage extends React.Component<any, Map<string, string>> {

  public componentWillMount() {
    const map = new Map<string, string>();
    map.set('aKey', 'aValue');
    this.setState(map);
  }

  public render() {

      return (
        <div className="home">
          <div className="greeting">
            Home page: {this.state.get('aKey')} // <-- I get an error here
          </div>
        </div>
      );
  }
}

Ошибка:

homePage.tsx:12 Uncaught TypeError: this.state.get is not a function
    at HomePage.render (homePage.tsx:12)
    at eval (ReactCompositeComponent.js:793)
    at measureLifeCyclePerf (ReactCompositeComponent.js:73)
    at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (

Состояние всегда должно быть простым объектом с ключом, поэтому вместо этого определите состояние
что-то вроде: { values: Map <string, string> } и читать
this.state.values.get('aKey')

Оп вр 29 сен. 2017 в 09:01 запись Януш Бялобжевски <
уведомления@github.com>:

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

импортировать * как React из 'реагировать';
экспорт класса по умолчанию HomePage расширяет React.Component> {

общедоступный компонентWillMount() {
const map = новая карта();
map.set('aKey', 'aValue');
это.setState(карта);
}

публичный рендер () {

  return (
    <div className="home">
      <div className="greeting">
        Home page: {this.state.get('aKey')} // <-- I get an error here
      </div>
    </div>
  );

}
}

Ошибка:

домашняя страница. tsx:12 Uncaught TypeError: this.state.get не является функцией
в HomePage.render (homePage.tsx:12)
в оценке (ReactCompositeComponent.js:793)
при измеренииLifeCyclePerf (ReactCompositeComponent.js:73)
в ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/14250#issuecomment-333047367 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/ABvGhM5hDyRNyUeZuIiGeTZk1N-rfuA4ks5snJW5gaJpZM4LuDWV
.

Спасибо, но кажется бессмысленным объявлять состояние как Readonly<S> , поскольку его вложенные члены не зависят от Readonly.

Возможно, в один прекрасный день Readonly будет применяться рекурсивно, но сейчас вам нужно убедиться, что вы справились с этим правильно. В вашем случае вы действительно должны объявить ReadonlyMap или

interface State {
    readonly [key: string]: string;
}

или вложенные:

interface State {
    map: { readonly [key: string]: string };
}

Мы можем использовать его только для глубокого чтения:

export type DeepReadonly<T> =
  T extends Array<any> ?
  ReadonlyArray<T[0]> :
  T extends Date ?
  T :
  T extends Function ?
  T :
  T extends object ?
  { readonly [P in keyof T]: DeepReadonly<T[P]> } :
  T;

export type Writable<T> =
  T extends ReadonlyArray<any> ?
  Array<WritableObject<T[0]>> :
  T extends Array<any> ?
  Array<WritableObject<T[0]>> :
  WritableObject<T>;

type WritableObject<T> =
  T extends Date ?
  T :
  T extends Function ?
  T :
  T extends object ?
  { -readonly [P in keyof T]: Writable<T[P]> } :
  T;
Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

lilling picture lilling  ·  3Комментарии

csharpner picture csharpner  ·  3Комментарии

Loghorn picture Loghorn  ·  3Комментарии

alisabzevari picture alisabzevari  ·  3Комментарии

tyv picture tyv  ·  3Комментарии