React Typescript 属性在类型"产品 |客户.ts(7053)



我正在用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>
);
}

formUtils

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 });
};

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

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 })
}

最新更新