Trying to add typescript to custom React Form Component and can't rid of mistake i don't know how to fix. TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ username: string; email: string; password: string; confirmPassword: string; }'. No index signature with a parameter of type 'string' was found on type '{ username: string; email: string; password: string; confirmPassword: string; }'.
I'm sure there is an answer somewhere, but i can't adjust it to my code as well as can't understand the basic of mistake. Please help me understand
App Component
import React, {useState} from 'react';
import s from './App.module.scss'
import Input from "./components/Input/Input";
function App() {
const [values, setValues] = useState({
username: '',
email: '',
password: '',
confirmPassword: '',
});
const inputs = [
{
id: "username",
name: "UserName",
type: "text",
placeholder: "Username",
},
{
id: "email",
name: "Email",
type: "text",
placeholder: "@gmail.com",
},
]
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValues({...values, [e.target.name]: e.target.value});
}
return (
<div className={s.app}>
<form action="">
{inputs.map((input) => (
<Input
key={input.id}
id={input.id}
name={input.name}
placeholder={input.placeholder}
type={input.type}
value={values[input.name]}
onChange={handleChange}
/>
))}
</form>
</div>
);
}
export default App;
Input Component
import React, {FC} from 'react';
import s from './Input.module.scss';
interface InputProps {
id: string,
name: string,
placeholder: string,
type: string,
value: string,
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void,
}
const Input: FC<InputProps> = ({id, name, placeholder, type, value, onChange}) => {
return (
<div className={s.input}>
<label htmlFor={id}>
{name}
</label>
<input
id={id}
type={type}
placeholder={placeholder}
value={value}
onChange={onChange}
/>
</div>
);
};
export default Input;
Source code: https://github.com/Endo-Resu/react-custom-form
Thank you for ur time, advices and help
CodePudding user response:
Your variable values
is of the type { username: string; email: string; password: string; confirmPassword: string; }
. Accessing its contents can be done by calling values.username
or values['username']
.
Typescript can even infer something like this:
let key = 'username'
let username = values[key]
This is because key
does not have the general type string
in this instance, but is known to be of the type 'username'
, which is known to be able to index values
.
Your code contains the line value={values[input.name]}
. The variable input.name
is of the type string
. However, values
cannot be indexed by any string, only specific ones. Typescript only knowns that input.name
is a string
. If you are certain, that your string can index the type, you can typecast the string with index.name as keyof typeof values
and typescript should stop complaining.
Notice, however, that you are trying to use input.name
as the key for values
, but the names are "UserName"
and "Email"
. You should use input.id
instead as the ids match the actual keys. Capitalisation is important.
Now the result of values[index.name as keyof typeof values]
may still be of any
type, because typescript does not know which value is actually returned. Because everything in values
is a string you can just typecast is to string. In total you get values[index.name as keyof typeof values] as string
.
CodePudding user response:
Solution:
App component
import React, { useState } from "react";
import s from "./App.module.scss";
import Input from "./components/Input/Input";
function App() {
const [values, setValues] = useState({
username: "",
email: "",
password: "",
confirmPassword: ""
});
const inputs: {
id: string;
name: keyof typeof values;
type: string;
placeholder: string;
}[] = [
{
id: "username",
name: "username",
type: "text",
placeholder: "Username"
},
{
id: "email",
name: "email",
type: "text",
placeholder: "@gmail.com"
}
];
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [e.target.name]: e.target.value });
};
return (
<div className={s.app}>
<form action="">
{inputs.map((input) => (
<Input
key={input.id}
id={input.id}
name={input.name}
placeholder={input.placeholder}
type={input.type}
value={values[input.name]}
onChange={handleChange}
/>
))}
</form>
</div>
);
}
export default App;
Explanation:
As the previous answer explained, the error is in this line value={values[input.name]}
and the reason for the error is that the object values
cannot be indexed by any random string, the index has to be one of values
declared properties (like username
).
The solution is to give inputs.name
a type that can be used as index of values
:
const inputs: {
id: string;
name: keyof typeof values;
type: string;
placeholder: string;
}[] = ...
Now the property name
has a type of whatever keys in values
. Now input.name
can be used to index values
.