【问题标题】:typesafe select onChange event using reactjs and typescripttypesafe 使用 reactjs 和 typescript 选择 onChange 事件
【发布时间】:2016-01-20 06:30:05
【问题描述】:

我已经弄清楚如何使用丑陋的事件强制转换来将事件处理程序绑定到 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>
        );
    }
}

【问题讨论】:

  • 类型安全是什么意思?
  • 我猜这意味着由打字稿编译并检查所有变量的赋值是否符合它们的类型。我们说的是打字稿,当然不是 javascript

标签: javascript reactjs typescript


【解决方案1】:

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

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

然后我将 React.FormEvent 更改为 React.ChangeEvent 并有所帮助:

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

【讨论】:

  • 这应该是最重要的,如果我访问 e.target.value,FormEvent 总是显示错误
  • 这似乎已得到纠正,因为我现在可以成功访问event.target.value 而不会出现问题;它的类型为string,因此根据具体情况中使用的值类型,可能需要进行双重转换。例如event.target.value as unknown as number.
【解决方案2】:

自从升级我的类型以响应 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>
        );
    }
}

【讨论】:

【解决方案3】:

更新:React 的官方类型定义已经将事件类型作为泛型类型包含了一段时间,因此您现在可以进行完整的编译时检查,而这个答案已经过时了。 p>


是否可以在不强制转换的情况下以类型安全的方式检索值?

是的。如果您确定您的处理程序附加到的元素,您可以这样做:

<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'
}

Live demo

TypeScript 编译器将允许这种类型断言,因为 HTMLSelectElement 是一个 EventTarget。之后,它应该是类型安全的,因为您知道 e.target 是一个 HTMLSelectElement,因为您只是将事件处理程序附加到它。

然而,为了保证类型安全(在这种情况下,重构时是相关的),还需要检查实际的运行时类型:

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

【讨论】:

  • 我认为这比强制转换为 any 要好,但您仍在使用类型断言,而这正是我试图避免的。
  • 是的,但是由于您完全确定e.targetHTMLSelectElement,因此此类型断言是安全,并且所有后续类型检查本质上都是安全的,因为好吧,比如target.value。如果你想让它绝对防弹,你也可以对其类型进行运行时检查,如果e.target 不是正确的类型,则抛出TypeError。这实际上永远不会发生,但它增加了一个额外的保证类型安全层。
  • stackoverflow.com/questions/260626/what-is-type-safe 我对 typesafe 的理解是类型检查是由编译器强加的。这意味着没有类型断言并且不依赖于运行时异常。
  • 我之前评论说事件处理程序应该是公共的,而不是私有的。从那以后我意识到这是不正确的。即使我们在运行时有私有方法(希望我们会 in the future),私有方法在这里仍然可以正常工作,因为 React 不需要直接调用私有方法,而只需要你传递的箭头函数作为onChange 道具。
【解决方案4】:

在我的情况下,onChange 事件被输入为React.ChangeEvent&lt;HTMLSelectElement&gt;

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

【讨论】:

    【解决方案5】:

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

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

    或者您可以像这样转换value 属性和event.target

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

    编辑:

    最后,您可以在单独的 .d.ts 文件中定义 EventTarget.value 是什么。但是,该类型必须与在其他地方使用的地方兼容,并且无论如何您最终都会再次使用any

    globals.d.ts

    interface EventTarget {
        value: any;
    }
    

    【讨论】:

    • 我希望完全摆脱“as any”演员阵容。
    • 感谢您的想法,但这仍然不能回答我的问题。我认为答案是需要修改 react 的 .d.ts,以便 SELECT 元素的 onChange 签名使用新的 SelectFormEvent。否则,正如您指出的那样,它需要演员表。我想我可以将它封装在一个 MYSELECT 标记中。
    • 好的,别忘了回答并接受你的回答:)Self answer
    • 添加EventTarget.value 的更好方法是增加全局 eventTarget,这可以通过任何 .ts 文件完成:declare global { interface EventTarget { value: any; }}
    【解决方案6】:

    有效:

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

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

        据我所知,目前这是不可能的 - 总是需要演员表。

        为了使其成为可能,需要修改 react 的 .d.ts,以便 SELECT 元素的 onChange 签名使用新的 SelectFormEvent。新的事件类型将公开目标,这会公开价值。那么代码可以是类型安全的。

        否则总是需要转换为 any。

        我可以将所有这些都封装在一个 MYSELECT 标记中。

        【讨论】:

          【解决方案9】:

          除了@thoughtrepo 的回答:

          除非我们在 React 中没有明确的类型事件,否则为输入控件提供一个特殊的目标接口可能会很有用:

          export interface FormControlEventTarget extends EventTarget{
              value: string;
          }
          

          然后在你的代码中转换成这种类型的地方适合有 IntelliSense 支持:

           import {FormControlEventTarget} from "your.helper.library"
          
           (event.target as FormControlEventTarget).value;
          

          【讨论】:

            猜你喜欢
            • 2018-04-25
            • 2017-11-29
            • 1970-01-01
            • 1970-01-01
            • 2022-01-13
            • 2016-08-05
            • 1970-01-01
            • 1970-01-01
            • 2019-02-28
            相关资源
            最近更新 更多