Given following jsx file:
/* @flow */
var TheInput = React.createClass({
getInitialState() {
return {quantity: 0};
},
handleChange(e : Event) {
console.log(e.target.value);
},
render() {
return (
<form>
<input type="number" min="0" value={this.state.quantity} onChange={this.handleChange} />
</form>
);
}
});
React.render(
<TheInput />,
document.body
);
$ flow
/Users/user/app/flow-test/reactElement.js:7:21,34: property value
Property not found in
/private/var/folders/p7/pxfd7rs173nbt14rz38hd7dr0000gn/T/flow_user/flowlib_377ed47d/lib/dom.js:73:13,23: EventTarget
I would like to know how to use e.target.value
, without setting the argument e
as any
.
Ideally, EvenetTarget would inherit, but would need to be more than just an HTMLElement:
Element, document, and window are the most common event targets, but other objects can be event targets too, for example XMLHttpRequest, AudioNode, AudioContext, and others.
from mozilla
You could do something like
handleChange(e : Event) {
var target = e.target;
if (target instanceof HTMLInputElement) {
console.log(target.value);
}
},
alternatively you could throw if target is the wrong type.
Ah, hadn't thought of instance of. Will give that a whirl
As an alternative, would having a DomElementEvent which extents Event make sense? and then type react events off that? While a dom event can have any target, in the context of react, we know it will always have an element (or am I missing some scenarios where you'd have an xmlhttp object or something other than an element?)
Sent from my mobile
On Thu, Feb 12, 2015 at 6:00 pm, Gabe Levi <[email protected]notifications@github.com> wrote:
You could do something like
handleChange(e : Event) {
var target = e.target;
if (target instanceof HTMLInputElement) {
console.log(target.value);
}
},
alternatively you could throw if target is the wrong type.
Reply to this email directly or view it on GitHubhttps://github.com/facebook/flow/issues/218#issuecomment-74119319.
I forget the situations in which event.target
will be undefined...a quick google says IE 6-8 but I suppose there may be others. So even if we can generally assume that it won't be undefined, that still wouldn't really help the above example, since not all DOM elements have the value
field.
It would be nice if we could statically infer what which targets a given event handler might see. I'm sure doing this in a complete manner would be really hard, but I bet we could recognize the common ways of attaching a handler. It would be nice if you could just write
function handleChange(e : Event<HTMLInputElement>) { ... }
This would probably be pretty far off if we do attempt it though.
Yeah ā that would be great ā but Iād imagine hard..
In terms of where the event objects might be null ā Iād assumed we were only talking the normalized events provided via React ā so basically discount ie6/7 ā rather than apply to all dom events, but for now will just try the instance of check
Oh this made me notice that we forgot to put SyntheticEvent
(the type of react events) in react.js. I've created a PR to merge it in: https://github.com/facebook/flow/pull/260
yeah - that'd be good - though looks like its still saying currentTarget => EventTarget rather than DOMEventTarget, so currentTarget should always be a vaild DOMElement - or am I missing something?
I believe that EventTarget
and DOMEventTarget
are two names for the same interface (though Flow just supports EventTarget
), though it's possible that I'm missing something too. We could certainly type
currentTarget: EventTarget & Element;
(saying that the target is both a EventTarget
and an Element
) but honestly I'm not sure if that's true. Are there some docs I can reference?
Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.
Both target
and currentTarget
are officially just EventTarget
according to the latest DOM spec. You can "know" that currentTarget
will be an HTMLElement
because the handler happens to be only attached to an actual element, but that doesn't seem provable to the type system.
is if (target instanceof HTMLInputElement) {
still the best way to type events? For those not targeting old explorer versions this is a little bit cumbersome.
You can just assert the target type, i.e.
onChange={({target}: {target: HTMLInputElement}) => doSomething(target)}
Not saying that's the most elegant solution, but I find it a bit more clear.
I added this to my local typings library which has worked fine:
declare class ElementEvent<E> extends Event {
target: E
}
Can be used in a form callback like this:
onChange(e: ElementEvent<HTMLInputElement>) {
const value = e.target.value;
}
Good suggestion from @joarwilk, I think that's how people should handle this. In addition, you can define
declare type DOMEvent = ElementEvent<HTMLInputElement>;
so you can just use DOMEvent which is a lot cleaner.
@joarwilk & @cvoege With Flowtype v0.33.0 your suggestion gives me this:
[No file]:0
inconsistent use of library definitions
1: declare class ElementEvent<E> extends Event {
^ E. This type is incompatible with. See lib: tools/flow/types/React.js:1
141: target: EventTarget;
^^^^^^^^^^^ EventTarget. See lib: /private/tmp/flow/flowlib_7dd198c/dom.js:141
[No file]:0
inconsistent use of library definitions
141: target: EventTarget;
^^^^^^^^^^^ EventTarget. This type is incompatible with. See lib: /private/tmp/flow/flowlib_7dd198c/dom.js:141
1: declare class ElementEvent<E> extends Event {
^ some incompatible instantiation of `E`. See lib: tools/flow/types/React.js:1
Found 2 errors
@hannupekka I'm getting the same error with @cvoege's suggestion.
Here's another way of doing a similar thing
declare type ElementEventTemplate<E> = {
target: E
} & Event;
declare type InputEvent = ElementEventTemplate<HTMLInputElement>;
And then in your callbacks:
onChange(e: InputEvent) {
const value = e.target.value;
}
@joarwilk Nice, thanks!
It looks like this issue will be resolved by #2100 using SyntheticInputEvent
, which appears to be due for release in v0.35 (see 167b126).
š
SyntheticInputEvent
from v0.36 solved it for me.
@skovhus how do you use it? do I need to import it somehow? is it supported in webstorm?
thanks!
@petr001
how do you use it? do I need to import it somehow?
It is globally available, like string
, any
, etc. Just ensure that you are using newest Flow version.
is it supported in webstorm?
Depends if they support newest flow version, but I would assume so. Atom works, except that eslint doesn't recognise SyntheticInputEvent
yet.
@skovhus Thanks!
It seems that WebStorm does not support it yet. But InputEvent
works well for the moment...
Why does SyntheticInputEvent not also declare currentTarget as HTMLInputElement?
I have the same question as allenylzhou: Why _doesn't_ SyntheticInputEvent declare currentTarget as HTMLInputElement? I'd even expect target
to not necessarily be the input, either.
SyntheticInputEvent sounds like it's used in testing to simulate events.
Regarding currentTarget I filed a new issue here:
https://github.com/facebook/flow/issues/4381
@SEAPUNK @allenylzhou the reason is because target
is the source of the event while currentTarget
is the element the event listener is attached to. So it is guaranteed that target
will be an HTMLInputElement
, but currentTarget
could be either an HTMLInputElement
or any HTMLElement
that can contain an HTMLInputElement
.
@jcready Is listening to bubbling input events in React's virtual DOM even possible? Otherwise, I don't see any case where your described scenario can happen.
@SEAPUNK absolutely:
<div onInput={(e) => console.log(e.target, e.currentTarget)}>
<input type="text" />
</div>
Huh, I didn't expect onInput to work on non-input elements. Makes sense, then.
Just wondering, why SyntheticEvent<HTMLInputElement>
will not allow event.target.value
? I'm not telling exactly that the event is going to be associated to an input element with that annotation?
Also, is this a good solution?
(evt: { ...Event, target: HTMLInputElement }) => evt.target.value;
@FezVrasta I'm not sure .input
is a property on HTMLInputElements, is it? I think you mean .value
?
Also, event.target is the element that originally triggered the event, whereas event.currentTarget is the element that your event listener is bound to. So you probably want event.currentTarget.value
- https://flow.org/try/#0GYVwdgxgLglg9mABACwIZgCYBsCmBhLGCAawAocA3HMKALkQGUBPG5HWCAUSpoB4AJACoBZADIBJMAAcQUTrgC21KAD4AlIgDeAKESIICAM5xcAOixwA5uR5RTEEACdHywakeX2piqiwgcatoAvkA
Sorry I meant value
š Anyway I'll try, thanks
handleScroll = (e: SyntheticInputEvent<EventTarget>) => {e.target.scrollHeight..
worked for me
I run into the same issue:
handleKeyNavigation = (e: SyntheticKeyboardEvent<HTMLElement>) => {
e.target.getAttribute(...);
}
Cannot call e.target.getAttribute because property getAttribute is missing in EventTarget [1].
As @joarwilk suggested this solves the issue:
```javascript
declare type SyntheticKeyboardEventElement
target: E
} & SyntheticKeyboardEvent
handleKeyNavigation = (e: SyntheticKeyboardEventElement
e.target.getAttribute(...);
}
````
Most helpful comment
You could do something like
alternatively you could throw if target is the wrong type.