poldark
poldark

Reputation: 43

how fix Element implicitly has an 'any' type because expression of type 'string' can't be used to index type?

Trying out TypeScript for a React project and I'm stuck on this error.

TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ paymentMethod: string; amount: string; receiptCode: string; paymentDate: string; paymentSourceID: string; }'.   No index signature with a parameter of type 'string' was found on type '{ paymentMethod: string; amount: string; receiptCode: string; paymentDate: string; paymentSourceID: string; }'

when i try setInputs()

setInputs(inputs => ({...inputs, [inputs.field[eventName]]: eventValue}));

and this is my component.

import React, {useState} from 'react';
import {DatePicker} from "jalali-react-datepicker";
import axios from "axios";
import PaymentFormClasses from './PaymentForm.module.css';
import PaymentCard from "../../Cards/PaymentCard/PaymentCard";
import Card from "../../Cards/Card";

interface PaymentForm {
    field: {
        paymentMethod: string,
        amount: string,
        receiptCode: string,
        paymentDate: string,
        paymentSourceID: string
    }
}

const PaymentForm = () => {
    const [inputs, setInputs] = useState<PaymentForm>({
        field: {
            paymentMethod: '',
            amount: '',
            receiptCode: '',
            paymentDate: '',
            paymentSourceID: 'paymentSourceID'
        }
    });

    const changedHandler = (e: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLSelectElement>) => {
        console.log(typeof e.target.name);
        const eventName = e.target.name;
        const eventValue = e.target.value;

        setInputs(inputs => ({...inputs, [inputs.field[eventName]]: eventValue}));
        console.log(e.target.value);
    }
   

    return (
        <>

        </>
    );
};

export default PaymentForm;

Upvotes: 0

Views: 1615

Answers (2)

Linda Paiste
Linda Paiste

Reputation: 42228

You need to assert that e.target.name, which is always just string, is in fact a valid key of inputs.field.

const eventName = e.target.name as keyof PaymentForm['field'];

However your update syntax is not right. You are setting top-level properties of inputs and you are using the value of inputs.field[eventName] as the property name.

You could do a nested update:

setInputs(inputs => ({ 
  ...inputs, 
  field: { 
    ...inputs.field, 
    [eventName]: eventValue 
  }
}));

But why do we need to have this field property at all? It's the only property in inputs so why don't we just make it be the entire state.

interface Fields {
  paymentMethod: string;
  amount: string;
  receiptCode: string;
  paymentDate: string;
  paymentSourceID: string;
}

const PaymentForm = () => {
  const [fields, setFields] = useState<Fields>({
    paymentMethod: "",
    amount: "",
    receiptCode: "",
    paymentDate: "",
    paymentSourceID: "paymentSourceID"
  });

  const changedHandler = (
    e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
  ) => {
    setFields((fields) => ({
      ...fields,
      [e.target.name as keyof Fields]: e.target.value
    }));
  };

  return <></>;
};

Upvotes: 2

Josh
Josh

Reputation: 841

Your eventName can be a union of the field keys. Try this.

type EventName = 'paymentMethod' | 'amount' | 'recieptCode' ...etc;
const eventName = e.target.name as EventName; // OR

const eventName = e.target.name as keyof PaymentForm['field'];

Upvotes: 1

Related Questions