Подтвердить что ты не робот

Typeafe select onChange событие с использованием reactjs и typescript

Я выяснил, как связать обработчик события с элементом 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>
        );
    }
}
4b9b3361

Ответ 1

Начиная с обновления моих типировок, чтобы отреагировать на 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>
        );
    }
}

Ответ 2

Я попытался использовать 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);
}

Ответ 3

Обновление: официальные определения типов для 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.");
}

Ответ 4

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

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

Или вы можете использовать свойство value, а также event.target следующим образом:

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

Edit:

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

globals.d.ts

interface EventTarget {
    value: any;
}

Ответ 5

он работает:

type HtmlEvent = React.ChangeEvent<HTMLSelectElement>

const onChange: React.EventHandler<HtmlEvent> = 
   (event: HtmlEvent) => { 
       console.log(event.target.value) 
   }

Ответ 6

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

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

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

Я могу инкапсулировать все это в тег MYSELECT.

Ответ 7

В дополнение к @thoughtrepo ответ:

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

export interface FormControlEventTarget extends EventTarget{
    value: string;
}

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

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

 (event.target as FormControlEventTarget).value;

Ответ 8

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