'Typescript input onchange event.target.value
In my react and typescript app, I use:
onChange={(e) => data.motto = (e.target as any).value}
How do I correctly define the typings for the class, so I wouldn't have to hack my way around the type system with any
?
export interface InputProps extends React.HTMLProps<Input> {
...
}
export class Input extends React.Component<InputProps, {}> {
}
If I put target: { value: string };
I get :
ERROR in [default] /react-onsenui.d.ts:87:18
Interface 'InputProps' incorrectly extends interface 'HTMLProps<Input>'.
Types of property 'target' are incompatible.
Type '{ value: string; }' is not assignable to type 'string'.
Solution 1:[1]
Generally event handlers should use e.currentTarget.value
, e.g.:
onChange = (e: React.FormEvent<HTMLInputElement>) => {
const newValue = e.currentTarget.value;
}
You can read why it so here (Revert "Make SyntheticEvent.target generic, not SyntheticEvent.currentTarget.").
UPD: As mentioned by @roger-gusmao ChangeEvent
more suitable for typing form events.
onChange = (e: React.ChangeEvent<HTMLInputElement>)=> {
const newValue = e.target.value;
}
Solution 2:[2]
the correct way to use in TypeScript is
handleChange(e: React.ChangeEvent<HTMLInputElement>) {
// No longer need to cast to any - hooray for react!
this.setState({temperature: e.target.value});
}
render() {
...
<input value={temperature} onChange={this.handleChange} />
...
);
}
Follow the complete class, it's better to understand:
import * as React from "react";
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
interface TemperatureState {
temperature: string;
}
interface TemperatureProps {
scale: string;
}
class TemperatureInput extends React.Component<TemperatureProps, TemperatureState> {
constructor(props: TemperatureProps) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
// handleChange(e: { target: { value: string; }; }) {
// this.setState({temperature: e.target.value});
// }
handleChange(e: React.ChangeEvent<HTMLInputElement>) {
// No longer need to cast to any - hooray for react!
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature} onChange={this.handleChange} />
</fieldset>
);
}
}
export default TemperatureInput;
Solution 3:[3]
You can do the following:
import { ChangeEvent } from 'react';
const onChange = (e: ChangeEvent<HTMLInputElement>)=> {
const newValue = e.target.value;
}
Solution 4:[4]
as HTMLInputElement
works for me
Solution 5:[5]
The target
you tried to add in InputProps
is not the same target
you wanted which is in React.FormEvent
So, the solution I could come up with was, extending the event related types to add your target type, as:
interface MyEventTarget extends EventTarget {
value: string
}
interface MyFormEvent<T> extends React.FormEvent<T> {
target: MyEventTarget
}
interface InputProps extends React.HTMLProps<Input> {
onChange?: React.EventHandler<MyFormEvent<Input>>;
}
Once you have those classes, you can use your input component as
<Input onChange={e => alert(e.target.value)} />
without compile errors. In fact, you can also use the first two interfaces above for your other components.
Solution 6:[6]
we can also use the onChange event fire-up with defined types(in functional component) like as follows:
const handleChange = (
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => {
const name = e.target.name;
const value = e.target.value;
};
Solution 7:[7]
I use something like this:
import { ChangeEvent, useState } from 'react';
export const InputChange = () => {
const [state, setState] = useState({ value: '' });
const handleChange = (event: ChangeEvent<{ value: string }>) => {
setState({ value: event?.currentTarget?.value });
}
return (
<div>
<input onChange={handleChange} />
<p>{state?.value}</p>
</div>
);
}
Solution 8:[8]
When using Child Component We check type like this.
Parent Component:
export default () => {
const onChangeHandler = ((e: React.ChangeEvent<HTMLInputElement>): void => {
console.log(e.currentTarget.value)
}
return (
<div>
<Input onChange={onChangeHandler} />
</div>
);
}
Child Component:
type Props = {
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
}
export Input:React.FC<Props> ({onChange}) => (
<input type="tex" onChange={onChange} />
)
Solution 9:[9]
An alternative that has not been mentioned yet is to type the onChange function instead of the props that it receives. Using React.ChangeEventHandler:
const stateChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
console.log(event.target.value);
};
Solution 10:[10]
Here is a way with ES6 object destructuring, tested with TS 3.3.
This example is for a text input.
name: string = '';
private updateName({ target }: { target: HTMLInputElement }) {
this.name = target.value;
}
Solution 11:[11]
This is when you're working with a FileList
Object:
onChange={(event: React.ChangeEvent<HTMLInputElement>): void => {
const fileListObj: FileList | null = event.target.files;
if (Object.keys(fileListObj as Object).length > 3) {
alert('Only three images pleaseeeee :)');
} else {
// Do something
}
return;
}}
Solution 12:[12]
Thanks @haind
Yes HTMLInputElement
worked for input field
//Example
var elem = e.currentTarget as HTMLInputElement;
elem.setAttribute('my-attribute','my value');
elem.value='5';
This HTMLInputElement
is interface is inherit from HTMLElement
which is inherited from EventTarget
at root level. Therefore we can assert using as
operator to use specific interfaces according to the context like in this case we are using HTMLInputElement
for input field other interfaces can be HTMLButtonElement
, HTMLImageElement
etc.
For more reference you can check other available interface here
- Web API interfaces by Mozilla
- Interfaces in External Node Modules by Microsoft
Solution 13:[13]
You no need to type if you do this:
<input onChange={(event) => { setValue(e.target.value) }} />
Because if you set a new value with the arrow function directly in the html tag, typescript will understand by default the type of event.
Solution 14:[14]
function handle_change(
evt: React.ChangeEvent<HTMLInputElement>
): string {
evt.persist(); // This is needed so you can actually get the currentTarget
const inputValue = evt.currentTarget.value;
return inputValue
}
And make sure you have "lib": ["dom"]
in your tsconfig
.
Solution 15:[15]
This works for me also it is framework agnostic.
const handler = (evt: Event) => {
console.log((evt.target as HTMLInputElement).value))
}
Solution 16:[16]
import { NativeSyntheticEvent, TextInputChangeEventData,} from 'react-native';
// Todo in java script
const onChangeTextPassword = (text : any) => {
setPassword(text);
}
// Todo in type script use this
const onChangeTextEmail = ({ nativeEvent: { text },}: NativeSyntheticEvent<TextInputChangeEventData>) => {
console.log("________ onChangeTextEmail _________ "+ text);
setEmailId(text);
};
<TextInput
style={{ width: '100%', borderBottomWidth: 1, borderBottomColor: 'grey', height: 40, }}
autoCapitalize="none"
returnKeyType="next"
maxLength={50}
secureTextEntry={false}
onChange={onChangeTextEmail}
value={emailId}
defaultValue={emailId}
/>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow