web-dev-qa-db-ja.com

reactjsとtypescriptを使用したtypesafe select onChangeイベント

イベントのanyいキャストをanyに使用して、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>
        );
    }
}
48
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>
        );
    }
}
55
davestevens

React.FormEvent<HTMLSelectElement>を使用しようとしましたが、コードにEventTargetが表示されていなくても、エディターでエラーが発生しました。

プロパティ 'value'は、タイプ 'EventTarget'の値には存在しません

次に、React.FormEventReact.ChangeEventに変更しました。

private changeName(event: React.ChangeEvent<HTMLSelectElement>) {
    event.preventDefault();
    this.props.actions.changeName(event.target.value);
}
40
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'
}

ライブデモ

HTMLSelectElementEventTargetであるため、TypeScriptコンパイラはこのタイプアサーションを許可します。その後、e.targetHTMLSelectElementであることがわかっているため、タイプセーフになるはずです。イベントハンドラー。

ただし、guaranteetype-safety(この場合、リファクタリング時に関連する)には、実際のruntime-typeを確認する必要もあります。

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

Edit:

最後に、EventTarget.valueが別の.d.tsファイルにあるものを定義できます。ただし、他の場所で使用される場合は、型に互換性がなければならず、とにかくanyを再び使用することになります。

globals.d.ts

interface EventTarget {
    value: any;
}
9
thoughtrepo

できます:

type HtmlEvent = React.ChangeEvent<HTMLSelectElement>

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

私が知る限り、これは現在不可能です-キャストが常に必要です。

それを可能にするには、reactの.d.tsを変更して、SELECT要素のonChangeのシグネチャが新しいSelectFormEventを使用するようにする必要があります。新しいイベントタイプは、値を公開するターゲットを公開します。その場合、コードはタイプセーフになります。

そうしないと、常にanyへのキャストが必要になります。

これらすべてを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 });
}
2
Marco Lackovic

私の場合、onChangeイベントはReact.ChangeEventと入力されました。

onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => {
           console.warn('onChange TextInput value: ' + e.target.value);
           } 
         }
0
Jackkobec

@thoughtrepoの答えに加えて:

Reactにイベントを確実に入力しない限り、入力コントロール用の特別なターゲットインターフェイスを使用すると便利です。

export interface FormControlEventTarget extends EventTarget{
    value: string;
}

そして、このタイプにキャストするコードでIntelliSenseをサポートするのに適しています:

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

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