So the event received by an onBeforeInput
handler is typed as React.FormEvent<HTMLInputElement>
. This is quite a general type, and doesn't include the data
property.
As far as I'm aware the events that onBeforeInput
receives (nativeEvent
s being KeyboardEvent
in Firefox, TextEvent
in Chrome) will have the data
property.
What's the right way to write a handler that uses event.data
without TypeScript plaining that Property 'data' does not exist on type 'FormEvent<HTMLInputElement>'
?
onBeforeInput={(e) => {
handleInput(e.data);
e.preventDefault();
}}
So the event received by an onBeforeInput
handler is typed as React.FormEvent<HTMLInputElement>
. This is quite a general type, and doesn't include the data
property.
As far as I'm aware the events that onBeforeInput
receives (nativeEvent
s being KeyboardEvent
in Firefox, TextEvent
in Chrome) will have the data
property.
What's the right way to write a handler that uses event.data
without TypeScript plaining that Property 'data' does not exist on type 'FormEvent<HTMLInputElement>'
?
onBeforeInput={(e) => {
handleInput(e.data);
e.preventDefault();
}}
Share
Improve this question
asked Jun 24, 2021 at 0:53
AcornAcorn
50.6k30 gold badges141 silver badges179 bronze badges
3 Answers
Reset to default 3I've found you can also do the following and you'll have no type errors:
onBeforeInput={(event: React.CompositionEvent<HTMLInputElement>) => {
const data = event.data
}}
If you want to see what the next value in the input would be with the next value you can do the following:
onBeforeInput={(event: React.CompositionEvent<HTMLInputElement>) => {
const str = event.currentTarget.value
const sub = event.data
const posStart = event.currentTarget.selectionStart || 0
const posEnd = event.currentTarget.selectionEnd || posStart
const nextValue = `${str.slice(0, posStart)}${sub}${str.slice(posEnd)}`
}}
I just check the type definition and found this,
type of e
is FormEvent<HTMLInputElement>
,
Then the FormEvent
is defined as,
interface FormEvent<T = Element> extends SyntheticEvent<T> {
}
The FormEvent
is extended the SyntheticEvent
, which defined as,
interface SyntheticEvent<T = Element, E = Event> extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
And when I check the definition of BaseSyntheticEvent
, I found this,
interface BaseSyntheticEvent<E = object, C = any, T = any> {
nativeEvent: E;
currentTarget: C;
target: T;
bubbles: boolean;
cancelable: boolean;
defaultPrevented: boolean;
eventPhase: number;
isTrusted: boolean;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopPropagation(): void;
isPropagationStopped(): boolean;
persist(): void;
timeStamp: number;
type: string;
}
Here we don't have a property call data
. Then I just saw that there is a type call, CompositionEvent
which extends SyntheticEvent
.
interface CompositionEvent<T = Element> extends SyntheticEvent<T, NativeCompositionEvent> {
data: string;
}
And it has the field data
;
So I did,
<input type="text" onBeforeInput={(e:SyntheticEvent) => {
let event = e as CompositionEvent;
console.log(event.data);
}} />
Or,
interface CustomEvent extends SyntheticEvent {
data ?: string
}
<input type="text" onBeforeInput={(event:CustomEvent) => {
console.log(event.data);
}} />
The correct way to do this is:
onBeforeInput={(e: React.FormEvent<HTMLInputElement>) => {
handleInput(e.nativeEvent.data);
e.preventDefault();
}}
This is because react sends back a wrapper (SyntheticEvent) around the native event. This SyntheticEvent does not have a data property, so typescript is telling you the truth. Luckily react still provides a reference to the native event at nativeEvent
. Keep in mind that typescript treats properties like data
as any
on the HTMLInputEvent so you'll need to be careful to handle all possible values that could exist there.