it-roy-ru.com

типSafe выберите событие onChange, используя реагировать и набирать текст

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

Возможно ли получить значение безопасным для типа способом без приведения к какому-либо типу?

import React = require('react');

interface ITestState {
    selectedValue: string;
}

export class Test extends React.Component<{}, ITestState> {

    constructor() {
        super();
        this.state = { selectedValue: "A" };
    }

    change(event: React.FormEvent) {
        console.log("Test.change");
        console.log(event.target); // in chrome => <select class="form-control" id="searchType" data-reactid=".0.0.0.0.3.1">...</select>

        // Use cast to any works but is not type safe
        var unsafeSearchTypeValue = ((event.target) as any).value;

        console.log(unsafeSearchTypeValue); // in chrome => B

        this.setState({
            selectedValue: unsafeSearchTypeValue
        });
    }

    render() {
        return (
            <div>
                <label htmlFor="searchType">Safe</label>
                <select className="form-control" id="searchType" onChange={ e => this.change(e) } value={ this.state.selectedValue }>
                    <option value="A">A</option>
                    <option value="B">B</option>
                </select>
                <h1>{this.state.selectedValue}</h1>
            </div>
        );
    }
}
36
davestevens

С тех пор, как я обновил свои наборы до 0.14.43 (точно не знаю, когда это было введено), тип React.FormEvent теперь является универсальным, и это устраняет необходимость в приведении.

import React = require('react');

interface ITestState {
    selectedValue: string;
}

export class Test extends React.Component<{}, ITestState> {

    constructor() {
        super();
        this.state = { selectedValue: "A" };
    }

    change(event: React.FormEvent<HTMLSelectElement>) {
        // No longer need to cast to any - hooray for react!
        var safeSearchTypeValue: string = event.currentTarget.value;

        console.log(safeSearchTypeValue); // in chrome => B

        this.setState({
            selectedValue: safeSearchTypeValue
        });
    }

    render() {
        return (
            <div>
                <label htmlFor="searchType">Safe</label>
                <select className="form-control" id="searchType" onChange={ e => this.change(e) } value={ this.state.selectedValue }>
                    <option value="A">A</option>
                    <option value="B">B</option>
                </select>
                <h1>{this.state.selectedValue}</h1>
            </div>
        );
    }
}
47
davestevens

Я попытался использовать React.FormEvent<HTMLSelectElement>, но это привело к ошибке в редакторе, хотя в коде нет EventTarget:

Свойство 'value' не существует для значения типа 'EventTarget'

Затем я изменил React.FormEvent на React.ChangeEvent, и это помогло:

private changeName(event: React.ChangeEvent<HTMLSelectElement>) {
    event.preventDefault();
    this.props.actions.changeName(event.target.value);
}
25
Sergei Basharov

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


Возможно ли получить значение безопасным для типа способом без приведения к какому-либо типу?

Да. Если вы уверены в элементе, к которому прикреплен ваш обработчик, вы можете сделать:

<select onChange={ e => this.selectChangeHandler(e) }>
    ...
</select>
private selectChangeHandler(e: React.FormEvent)
{
    var target = e.target as HTMLSelectElement;
    var intval: number = target.value; // Error: 'string' not assignable to 'number'
}

Живая демо

Компилятор TypeScript разрешит это утверждение типа, потому что HTMLSelectElement является EventTarget. После этого он должен быть безопасным с точки зрения типов, потому что вы знаете, что e.target является HTMLSelectElement, потому что вы просто прикрепили к нему обработчик событий.

Однако для гарантии безопасности типов (что в данном случае имеет значение при рефакторинге) также необходимо проверить фактический тип времени выполнения:

if (!(target instanceof HTMLSelectElement))
{
    throw new TypeError("Expected a HTMLSelectElement.");
}
12
John Weisz

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

var value: string = (event.target as any).value;

Или вы можете привести свойство value, а также event.target так:

var value = ((event.target as any).value as string);

Редактировать:

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

globals.d.ts

interface EventTarget {
    value: any;
}
11
thoughtrepo

оно работает: 

type HtmlEvent = React.ChangeEvent<HTMLSelectElement>

const onChange: React.EventHandler<HtmlEvent> = 
   (event: HtmlEvent) => { 
       console.log(event.target.value) 
   }
5
Петр Графинов

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

Чтобы сделать это возможным, необходимо изменить .d.ts of response, чтобы в сигнатуре onChange элемента SELECT использовался новый SelectFormEvent. Новый тип события выставляет цель, которая выставляет значение. Тогда код может быть безопасным.

В противном случае всегда будет необходимость в приведении к любому.

Я мог бы заключить все это в тег MYSELECT.

3
davestevens

JSX:

<select value={ this.state.foo } onChange={this.handleFooChange}>
    <option value="A">A</option>
    <option value="B">B</option>
</select>

TypeScript :

private handleFooChange = (event: React.FormEvent<HTMLSelectElement>) => {
    const element = event.target as HTMLSelectElement;
    this.setState({ foo: element.value });
}
0
Marco Lackovic

В дополнение к ответу @ мысльrepo:

Пока у нас нет определенно типизированных событий в React, может быть полезно иметь специальный целевой интерфейс для элементов управления вводом:

export interface FormControlEventTarget extends EventTarget{
    value: string;
}

И затем в вашем коде приведение к этому типу, где уместно иметь IntelliSense support:

 import {FormControlEventTarget} from "your.helper.library"

 (event.target as FormControlEventTarget).value;
0
Artru