Typescript: React event types

What is the correct type for React events. Initially I just used any for the sake of simplicity. Now, I am trying to clean things up and avoid use of any completely.

So in a simple form like this:

export interface LoginProps {
  login: {
    [k: string]: string | Function
    uname: string
    passw: string
    logIn: Function
  }
}
@inject('login') @observer
export class Login extends Component<LoginProps, {}> {
  update = (e: React.SyntheticEvent<EventTarget>): void => {
    this.props.login[e.target.name] = e.target.value
  }
  submit = (e: any): void => {
    this.props.login.logIn()
    e.preventDefault()
  }
  render() {
    const { uname, passw } = this.props.login
    return (
      <div id='login' >
        <form>
          <input
            placeholder='Username'
            type="text"
            name='uname'
            value={uname}
            onChange={this.update}
          />
          <input
            placeholder='Password'
            type="password"
            name='passw'
            value={passw}
            onChange={this.update}
          />
          <button type="submit" onClick={this.submit} >
            Submit
          </button>
        </form>
      </div>
    )
  }
}

What type do I use here as event type?

React.SyntheticEvent<EventTarget> does not seem to be working as I get an error that name and value do not exist on target.

More generalised answer for all events would be really appreciated.

Thanks

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The SyntheticEvent interface is generic:

interface SyntheticEvent<T> {
    ...
    currentTarget: EventTarget & T;
    ...
}

(Technically the currentTarget property is on the parent BaseSyntheticEvent type.)

And the currentTarget is an intersection of the generic constraint and EventTarget.
Also, since your events are caused by an input element you should use the ChangeEvent (in definition file, the react docs).

Should be:

update = (e: React.ChangeEvent<HTMLInputElement>): void => {
    this.props.login[e.currentTarget.name] = e.currentTarget.value
}

(Note: This answer originally suggested using React.FormEvent. The discussion in the comments is related to this suggestion, but React.ChangeEvent should be used as shown above.)

Method 2

The problem is not with the Event type, but that the EventTarget interface in typescript only has 3 methods:

interface EventTarget {
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
    dispatchEvent(evt: Event): boolean;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}

interface SyntheticEvent {
    bubbles: boolean;
    cancelable: boolean;
    currentTarget: EventTarget;
    defaultPrevented: boolean;
    eventPhase: number;
    isTrusted: boolean;
    nativeEvent: Event;
    preventDefault(): void;
    stopPropagation(): void;
    target: EventTarget;
    timeStamp: Date;
    type: string;
}

So it is correct that name and value don’t exist on EventTarget. What you need to do is to cast the target to the specific element type with the properties you need. In this case it will be HTMLInputElement.

update = (e: React.SyntheticEvent): void => {
    let target = e.target as HTMLInputElement;
    this.props.login[target.name] = target.value;
}

Also for events instead of React.SyntheticEvent, you can also type them as following: Event, MouseEvent, KeyboardEvent…etc, depends on the use case of the handler.

The best way to see all these type definitions is to checkout the .d.ts files from both typescript & react.

Also check out the following link for more explanations:
Why is Event.target not Element in Typescript?

Method 3

To combine both Nitzan’s and Edwin’s answers, I found that something like this works for me:

update = (e: React.FormEvent<EventTarget>): void => {
    let target = e.target as HTMLInputElement;
    this.props.login[target.name] = target.value;
}

Method 4

I think the simplest way is that:

type InputEvent = React.ChangeEvent<HTMLInputElement>;
type ButtonEvent = React.MouseEvent<HTMLButtonElement>;

update = (e: InputEvent): void => this.props.login[e.target.name] = e.target.value;
submit = (e:  ButtonEvent): void => {
    this.props.login.logIn();
    e.preventDefault();
}

Method 5

for update: event: React.ChangeEvent
for submit: event: React.FormEvent
for click: event: React.MouseEvent

Method 6

you can do like this in react

handleEvent = (e: React.SyntheticEvent<EventTarget>) => {
  const simpleInput = (e.target as HTMLInputElement).value;
  //for simple html input values
  const formInput = (e.target as HTMLFormElement).files[0];
  //for html form elements
}

Method 7

I have the following in a types.ts file for html input, select, and textarea:

export type InputChangeEventHandler = React.ChangeEventHandler<HTMLInputElement>
export type TextareaChangeEventHandler = React.ChangeEventHandler<HTMLTextAreaElement>
export type SelectChangeEventHandler = React.ChangeEventHandler<HTMLSelectElement>

Then import them:

import { InputChangeEventHandler } from '../types'

Then use them:

const updateName: InputChangeEventHandler = (event) => {
  // Do something with `event.currentTarget.value`
}
const updateBio: TextareaChangeEventHandler = (event) => {
  // Do something with `event.currentTarget.value`
}
const updateSize: SelectChangeEventHandler = (event) => {
  // Do something with `event.currentTarget.value`
}

Then apply the functions on your markup (replacing ... with other necessary props):

<input onChange={updateName} ... />
<textarea onChange={updateName} ... />
<select onChange={updateSize} ... >
  // ...
</select>

Method 8

For those who are looking for a solution to get an event and store something, in my case a HTML 5 element, on a useState here’s my solution:

const [anchorElement, setAnchorElement] = useState<HTMLButtonElement | null>(null);

const handleMenu = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) : void => {
    setAnchorElement(event.currentTarget);
};

Method 9

The following have the same type:

let event1: { target: { value: any } };
let event2: { target: HTMLInputElement } };


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x