类型“Product”上不存在React Typescript属性|客户的. ts(7053)

f0ofjuux  于 2022-12-14  发布在  TypeScript
关注(0)|答案(1)|浏览(144)

我正在使用typescript构建一个react应用程序,在该应用程序中我使用了客户和产品的数据,现在我想构建一个用于更新客户或产品的表单组件。
我希望将数据类型(Customer或Product)和数据ID属性传递给表单组件,并基于此呈现表单
我的数据类型是:

export interface Product {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

export interface Customer {
  id: string;
  firstName: string;
  lastName: string;
  city: string;
}

但是当我尝试为窗体中的项生成initialValues时-

const getInitialValues = (
  editedItem: Product | Customer,
  fields: (keyof Product | keyof Customer)[],
) => {
  return fields.reduce((acc, field) => {
    acc[field] = editedItem ? editedItem[field] : '';
    return acc;
  }, {} as { [key: string]: string });
};

打字员抱怨

Element implicitly has an 'any' type because expression of type '"id" | "name" | "price" | "quantity" | "firstName" | "lastName" | "city"' can't be used to index type 'Product | Customer'.
  Property 'name' does not exist on type 'Product | Customer'.ts(7053)

我认为这是因为typescript无法确定editedItem对象的类型,所以它假定它是any类型。
我该如何解决这个问题?
完整代码:EditForm组件

import { useState } from 'react';
import { useAppSelector } from '../../app/hooks';
import {
  customersLoadingSelector,
  customersSelector,
} from '../../features/customers/customersSlice';
import {
  productsLoadingSelector,
  productsSelector,
} from '../../features/products/productsSlice';
import { getField, getInitialValues } from '../../helpers/formUtils';
import { Customer, Product } from '../../types';
import { StyledEditFormContainer } from './EditForm.style';

export interface EditFormProps {
  editedId: string;
  editedType: 'product' | 'customer';
}

export default function EditForm({ editedId, editedType }: EditFormProps) {
  const products = useAppSelector(productsSelector);
  const customers = useAppSelector(customersSelector);

  const editedItem: Product | Customer | undefined =
    editedType === 'product'
      ? products.find((product) => product.id === editedId)
      : customers.find((customer) => customer.id === editedId);
  
  const fields = getField(editedType);

  const initialValues = getInitialValues(editedItem, fields);

  const [editValues, setEditValues] = useState(editInitialValues);

  const [editFormInputItem, setEditFormInputItem] = useState(editedItem);

  const handleEditFieldCancel = (field: keyof Product | keyof Customer) => {
    setEditValues((prev) => ({ ...prev, [field]: false }));
    setEditFormInputItem(editedItem);
  };

  const handleEditFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
  };

  const handleEditFieldSave = (field: keyof Product | keyof Customer) => {
    if (!editedItem) {
      return
    }
    
    if (editFormInputItem[field] === editedItem[field]) return;
    setEditValues((prev) => ({ ...prev, [field]: false }));
  }

  const isItemLoading =
    editedType === 'product'
      ? useAppSelector(productsLoadingSelector)
      : useAppSelector(customersLoadingSelector);

  if (isItemLoading) return <h1>Loading</h1>;
  if (!editedItem) return <h1>{editedType} not found</h1>;

  return (
    <StyledEditFormContainer onSubmit={handleEditFormSubmit}>
      {fields.map((field) => {
        return (
          <div key={field} className="edit-form__field">
            {editValues[field] ? (
              <>
                <label
                  className="edit-form__label"
                  htmlFor={`edit-${editedType}__${field}-input`}
                >
                  {field}
                </label>
                <input
                  className="edit-form__input"
                  id={`edit-${editedType}__${field}-input`}
                  type="text"
                  value={(editFormInputItem as any)[field]}
                  onChange={(e) => {
                    setEditFormInputItem(
                      (prev) =>
                        ({
                          ...prev,
                          [field]: e.target.value,
                        } as Product | Customer),
                    );
                    console.log(editFormInputItem);
                  }}
                />
                <button className="edit-form__button"
                  onClick={
                   () => handleEditFieldSave(field)
                  }
                >Save</button>
                <button
                  className="edit-form__button"
                  onClick={() => handleEditFieldCancel(field)}
                >
                  Cancel
                </button>
              </>
            ) : (
              <>
                <p>{field}:</p>
                <h1>{(editedItem as any)[field]}</h1>
                <button
                  onClick={() => {
                    setEditValues((prev) => ({
                      ...prev,
                      [field]: true,
                    }));
                  }}
                >
                  Edit
                </button>
              </>
            )}
          </div>
        );
      })}
    </StyledEditFormContainer>
  );
}

表单实用程序

import { EditFormProps } from '../components/EditForm/EditForm';
import { Customer, Product } from '../types';

export const getField = (
  editedType: EditFormProps['editedType'],
): (keyof Product | keyof Customer)[] => {
  if (editedType === 'product') {
    return ['name', 'price', 'quantity'];
  }
  return ['firstName', 'lastName', 'city'];
};

export const getInitialValues = (
  editedItem: Product | Customer | undefined,
  fields: (keyof Product | keyof Customer)[],
) => {
  return fields.reduce((acc, field) => {
    acc[field] = editedItem ? editedItem[field] : '';
    return acc;
  }, {} as { [key: string]: string });
};
odopli94

odopli941#

在这种情况下,泛型类型可能是更好的选择

const getInitialValues = <T, K extends keyof T>(editedItem: T, fields: K[]) => {
  return fields.reduce((acc, field: K) => {
    acc[field as string] = editedItem ? editedItem[field] : ''
    return acc
  }, {} as { [key: string]: any })
}

相关问题