[英]typesafe select onChange event using reactjs and typescript
I have figured out how to tie up an event handler on a SELECT element using an ugly cast of the event to any. 我已经想出如何使用事件的丑陋演员来绑定SELECT元素上的事件处理程序。
Is it possible to retrieve the value in a type-safe manner without casting to any? 是否可以以类型安全的方式检索值而不转换为任何值?
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>
);
}
}
Since upgrading my typings to react 0.14.43 (I'm not sure exactly when this was introduced), the React.FormEvent type is now generic and this removes the need for a cast. 由于升级我的打字以反应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>
);
}
}
I tried using React.FormEvent<HTMLSelectElement>
but it led to an error in the editor, even though there is no EventTarget
visible in the code: 我尝试使用
React.FormEvent<HTMLSelectElement>
但它导致编辑器出错,即使代码中没有可见的EventTarget
:
The property 'value' does not exist on value of type 'EventTarget'
'EventTarget'类型的值不存在属性'value'
Then I changed React.FormEvent
to React.ChangeEvent
and it helped: 然后我将
React.FormEvent
更改为React.ChangeEvent
,它帮助:
private changeName(event: React.ChangeEvent<HTMLSelectElement>) {
event.preventDefault();
this.props.actions.changeName(event.target.value);
}
Update: the official type-definitions for React have been including event types as generic types for some time now, so you now have full compile-time checking, and this answer is obsolete. 更新:React的官方类型定义已经将事件类型作为泛型类型包含了一段时间,因此您现在有完整的编译时检查,这个答案已经过时了。
Is it possible to retrieve the value in a type-safe manner without casting to any?
是否可以以类型安全的方式检索值而不转换为任何值?
Yes. 是。 If you are certain about the element your handler is attached to, you can do:
如果您确定您的处理程序所附加的元素,您可以执行以下操作:
<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'
}
The TypeScript compiler will allow this type-assertion, because an HTMLSelectElement is an EventTarget . TypeScript编译器将允许此类型断言,因为HTMLSelectElement是EventTarget 。 After that, it should be type-safe, because you know that e.target is an HTMLSelectElement , because you just attached your event handler to it.
之后,它应该是类型安全的,因为您知道e.target是一个HTMLSelectElement ,因为您只是将事件处理程序附加到它。
However, to guarantee type-safety (which, in this case, is relevant when refactoring), it is also needed to check the actual runtime-type: 但是,为了保证类型安全(在这种情况下,在重构时相关),还需要检查实际的运行时类型:
if (!(target instanceof HTMLSelectElement))
{
throw new TypeError("Expected a HTMLSelectElement.");
}
The easiest way is to add a type to the variable that is receiving the value, like this: 最简单的方法是向接收值的变量添加一个类型,如下所示:
var value: string = (event.target as any).value;
Or you could cast the value
property as well as event.target
like this: 或者你可以像这样
event.target
value
属性和event.target
:
var value = ((event.target as any).value as string);
Edit: 编辑:
Lastly, you can define what EventTarget.value
is in a separate .d.ts
file. 最后,您可以在单独的
.d.ts
文件中定义EventTarget.value
。 However, the type will have to be compatible where it's used elsewhere, and you'll just end up using any
again anyway. 但是,该类型必须与其他地方使用的类型兼容,并且无论如何你最终都会使用
any
。
globals.d.ts globals.d.ts
interface EventTarget {
value: any;
}
it works: 有用:
type HtmlEvent = React.ChangeEvent<HTMLSelectElement>
const onChange: React.EventHandler<HtmlEvent> =
(event: HtmlEvent) => {
console.log(event.target.value)
}
As far as I can tell, this is currently not possible - a cast is always needed. 据我所知,这目前是不可能的 - 总是需要演员。
To make it possible, the .d.ts of react would need to be modified so that the signature of the onChange of a SELECT element used a new SelectFormEvent. 为了使其成为可能,需要修改反应的.d.ts,以便SELECT元素的onChange的签名使用新的SelectFormEvent。 The new event type would expose target, which exposes value.
新事件类型将公开target,它会公开值。 Then the code could be typesafe.
然后代码可以是类型安全的。
Otherwise there will always be the need for a cast to any. 否则总是需要对任何人施展。
I could encapsulate all that in a MYSELECT tag. 我可以将所有内容封装在MYSELECT标记中。
JSX : JSX :
<select value={ this.state.foo } onChange={this.handleFooChange}>
<option value="A">A</option>
<option value="B">B</option>
</select>
TypeScript : TypeScript :
private handleFooChange = (event: React.FormEvent<HTMLSelectElement>) => {
const element = event.target as HTMLSelectElement;
this.setState({ foo: element.value });
}
In addition to @thoughtrepo's answer: 除了@ thoughtrepo的回答:
Until we do not have definitely typed events in React it might be useful to have a special target interface for input controls: 在我们在React中没有明确键入的事件之前,为输入控件设置一个特殊的目标接口可能很有用:
export interface FormControlEventTarget extends EventTarget{
value: string;
}
And then in your code cast to this type where is appropriate to have IntelliSense support: 然后在您的代码中转换为适合获得IntelliSense支持的此类型:
import {FormControlEventTarget} from "your.helper.library"
(event.target as FormControlEventTarget).value;
In my case onChange event was typed as React.ChangeEvent: 在我的情况下,onChange事件被输入为React.ChangeEvent:
onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => {
console.warn('onChange TextInput value: ' + e.target.value);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.