Опишите тип обработчика состояний с помощью React и Typescript - PullRequest
0 голосов
/ 06 февраля 2019

Я пытаюсь описать тип функции, которая обрабатывает событие изменения, чтобы отразить значение в состоянии.

Требуемая реализация будет выглядеть следующим образом:

handleChange: ChangeHandler<State> = field => value =>
    this.setState({ [field]: value });

Данное состояние:

interface State {
  title: string;
  description: string | null;
}

Эту функцию можно передать компоненту как:

<TextComponent onChange={this.handleChange('title')} />

Моя лучшая попытка решить эту проблему - создать следующий тип:

type ChangeHandler<S> = <K extends keyof S, V extends S[K]>(key: K) => (value: V) => void;

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

Ошибка компилятора:

[ts] Аргумент типа '{[x:строка]: V;} 'нельзя назначить параметру типа' State |((prevState: только для чтения, реквизиты: только для чтения) => State | Pick | null) |Выбрать <...> |null '.
Type' {[x: string]: V;} 'отсутствуют следующие свойства из типа' Pick ': заголовок, описание [2345]

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Основная причина

Это вызвано известной проблемой с TypeScript, когда имена вычисляемых свойств, состоящие из объединения литералов , расширены.

В handleChange тип field расширен с 'title' | 'description' до string, что приводит к сбою this.setState из-за способа определения this.setState.

Ниже приведено упрощенное определение setState:

function setState<K extends keyof State>(s: Pick<State, K>): void {
    console.log(s);
}

. При применении этого определения к handleChange, K выше становится 'title' | 'description', а параметр s становится Pick<State, 'title' | 'description'>, чтопо существу эквивалентен интерфейсу State.

Итак, учитывая, что наш field был расширен до string и this.setState в этом контексте принимает State, мы получим ошибку, которую вымы видим, как мы вызываем this.setState со следующим типом:

{ [x: string]: V }

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

Решение

Лучше всего здесь использовать утверждение типа , чтобы отключить ошибку, пока основнойпроверка типа.Обновленное определение приведено ниже:

let handleChange: ChangeHandler<State> = field => value =>
    setState({ [field]: value } as unknown as State);

let x = handleChange('title');

x('test'); // OK

x(50); // Error: should be string

См. ссылку на эту игровую площадку , содержащую приведенный выше код.


См. обсуждение по этому конкретному вопросуошибка в DefinitiveTyped для альтернативных решений / подходов.

0 голосов
/ 07 февраля 2019

Вы не должны определять V extends S[K], так как вы можете напрямую встроить это объявление следующим образом:

type ChangeHandler<S> = <K extends keyof S>(key: K) => (value: S[K]) => void;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...