typesafe 使用 reactjs 和 typescript 选择 onChange 事件

IT技术 javascript reactjs typescript
2021-03-25 17:03:37

我已经想出了如何使用丑陋的事件转换来将事件处理程序绑定到 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>
        );
    }
}
6个回答

我尝试使用React.FormEvent<HTMLSelectElement>但它导致编辑器中出现错误,即使EventTarget代码中没有可见:

“EventTarget”类型的值上不存在属性“value”

然后,我改变React.FormEventReact.ChangeEvent它帮助:

private changeName(event: React.ChangeEvent<HTMLSelectElement>) {
    event.preventDefault();
    this.props.actions.changeName(event.target.value);
}
这似乎已得到纠正,因为我现在可以event.target.value毫无问题地成功访问它的类型为string,因此可能需要根据具体情况中使用的值类型进行双重转换。例如event.target.value as unknown as number
2021-05-29 17:03:37
这应该是最重要的,如果我访问 e.target.value,FormEvent 总是显示错误
2021-06-02 17:03:37

由于将我的类型升级为 react 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>
        );
    }
}
戴夫,考虑更新你的例子。
2021-05-31 17:03:37
@MartinMajewski 我已经编辑使用 currentTarget (joequery.me/code/event-target-vs-event-currenttarget-30-seconds是一个很好的解释)
2021-06-08 17:03:37
截至 2016 年 12 月,必须使用 event.currentTarget.value 从表单中获取数据。
2021-06-12 17:03:37

更新: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.");
}
我之前评论说事件处理程序应该是公开的,而不是私有的。从那以后我意识到这是不正确的。即使我们在运行时有私有方法(希望我们将来会有),私有方法在这里仍然可以正常工作,因为 React 不需要直接调用私有方法,而只需要你传递的箭头函数作为onChange道具。
2021-05-31 17:03:37
是的,但是由于您完全确定它e.targetHTMLSelectElement,因此这种类型断言是安全的,并且所有后续类型检查本质上也是安全的,例如target.value. 如果你想让它绝对防弹,你也可以对它的类型进行运行时检查,并抛出一个TypeErrorife.target不是正确的类型。这实际上永远不会发生,但它增加了一个额外的类型安全保证
2021-06-10 17:03:37
我想这比强制转换为任何更好,但您仍在使用类型断言,这就是我试图避免的。
2021-06-15 17:03:37
stackoverflow.com/questions/260626/what-is-type-safe我对类型安全的理解是类型检查是由编译器强加的。这意味着没有类型断言,也不依赖于运行时异常。
2021-06-21 17:03:37

在我的情况下,onChange事件输入为React.ChangeEvent<HTMLSelectElement>

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

最简单的方法是向接收值的变量添加类型,如下所示:

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;
}
感谢您的想法,但这仍然没有回答我的问题。我认为答案是需要修改 react 的 .d.ts,以便 SELECT 元素的 onChange 签名使用新的 SelectFormEvent。否则,正如您指出的那样,它需要演员阵容。我想我可以将其封装在 MYSELECT 标签中。
2021-05-24 17:03:37
更好的添加方法EventTarget.value是增加全局 eventTarget,这可以通过任何 .ts 文件完成:declare global { interface EventTarget { value: any; }}
2021-05-30 17:03:37
我希望完全摆脱“as any”演员阵容。
2021-06-06 17:03:37
好的,不要忘记回答并接受您的回答:)自我回答
2021-06-13 17:03:37