这是我的第一篇帖子!
我正在使用GraphQL从Contentful获取产品列表,并通过筛选查找与用户点击匹配的产品。在将组件转换为TypeScript之前,一切都正常。我对TS有一些模糊的了解,而且几个月前才开始使用。
当我记录产品时,我会收到具有正确过滤数据的对象,但是当我尝试访问名称为“imagesCollection”的键时,我会收到以下TS错误消息:
属性“imagesCollection”在类型“{}”上不存在。ts(2339)
如果我添加 // @ts-ignore
,则产品图像将无问题地呈现。这是某种竞争条件吗?
非常感谢任何帮助,因为我已经花了很多时间尝试自己解决它。谢谢!
组件:
const Product = (props: any) => {
// Custom HOOK for fetching Contentful Data
const slug = props.match.params.slug;
const { product } = useContentful(query, slug);
console.log(product);
const [jacketColor, setJacketColor] = useState(null);
const [color, setColor] = useState(null);
useEffect(() => {
setJacketColor(
product.imagesCollection && product.imagesCollection.items[1].url
);
setColor(
// @ts-ignore
product.imagesCollection && product.imagesCollection.items[1].description
);
}, [product]);
const changeColor = (jacketColor: string) => {
// @ts-ignore
const filterHero = product.imagesCollection.items
.filter((heroJacket: { title: string }) =>
heroJacket.title.includes("hero")
)
.filter(
(item: { description: string }) => item.description === jacketColor
);
setJacketColor(filterHero[0].url);
setColor(filterHero[0].description);
};
const thumbnailImages = () => {
return (
// @ts-ignore
product.imagesCollection &&
// @ts-ignore
product.imagesCollection.items
.filter((jacket: queryProps) => jacket.title.includes("thumbnail"))
.map((jacket: { url: string; description: string }, index: number) => {
return (
<img
className={styles.thumbnailImages}
key={`products-${index}`}
src={jacket.url}
// @ts-ignore
alt={product.title}
onClick={() => changeColor(jacket.description)}
/>
);
})
);
};
return (
<>
<Header />
{!product ? (
<Loading />
) : (
<div className={styles.productsContainer}>
{/* @ts-ignore */}
<p className={styles.title}>{product && product.title}</p>
{documentToReactComponents(
// @ts-ignore
product.description && product.description.json,
RICHTEXT_OPTIONS
)}
{/* @ts-ignore */}
<p className={styles.price}>{`$${product && product.price}`}</p>
{
<img
className={styles.productImage}
// @ts-ignore
src={jacketColor}
// @ts-ignore
alt={product && product.title}
/>
}
<p className={styles.selectColor}>{`Select a colour: ${color}`}</p>
<div className={styles.thumbnailImagesWrapper}>
{thumbnailImages()}
</div>
</div>
)}
</>
);
};
export default Product;
获取数据的自定义钩子:
function useContentful(query: string, slug: string | null) {
const [products, setProducts] = useState([]);
const [product, setProduct] = useState({});
useEffect(() => {
fetch(graphqlURL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${REACT_APP_CDA_TOKEN}`,
},
body: JSON.stringify({ query }),
})
.then((res) => res.json())
.then(({ data }) => {
if (slug) {
data.arcteryxCollection.items.filter((item: queryProps) => {
return item.slug === slug && setProduct(item);
});
} else {
setProducts(data.arcteryxCollection.items);
}
})
.catch((err) => console.error(err));
}, [query, slug]);
return { product, products };
}
GraphQL 查询结构:
export const query = `
query {
arcteryxCollection {
items {
title
slug
description {
json
}
price
imagesCollection {
items {
url
title
description
}
}
}
}
}
`;
我正在导入的 TS 接口:
export interface queryProps {
title: string;
slug: string;
price: number;
description: {
json: {
content: {
value: string;
}[];
};
};
imagesCollection: {
items: {
url: string;
title: string;
description: string;
}[];
};
}