10

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 (nativeEvents 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 complaining that Property 'data' does not exist on type 'FormEvent<HTMLInputElement>'?

onBeforeInput={(e) => {
  handleInput(e.data);
  e.preventDefault();
}}

3 Answers 3

3

I'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)}`
}}
Sign up to request clarification or add additional context in comments.

1 Comment

Please note that selection does not work for inputs with type="number".
2

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

2 Comments

Thanks for taking a look! This doesn't seem like the worst workaround.. but seems wrong to have to cast the object to an unrelated event type..
I don't see an other solution except using base type of event handlers ( SyntheticEvent ) directly ( Something like using interface type instead of concrete type in oop )
0

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.

1 Comment

The data property does exist on the SyntheticEvent.. I'm not sure if it exists on the native event. Regardless, trying to access it on the native event just gives the same problem. "Property 'data' does not exist on type 'Event'".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.