and thanks for the time taken for reading this.
I'm trying to learn Typescript with react using eslint with the AirBnB config. Something as simple as mapping an array of objects and creating a custom functional component with each is giving me the next error:
Argument of type '(product: ProductType) => JSX.Element' is not assignable to parameter of type '(value: object, index: number, array: object[]) => Element'.
Here is the parent component ProductGrid:
const ProductGrid: React.FC = () => {
const products = [
{
id: "string1",
name: "string1",
price: {
formatted_with_symbol: "string1"
},
description: "string1",
},
...
];
return (
<div>
<Grid container spacing={3}>
<Grid item xs={12} md={6} lg={4}>
{products.map((product: ProductType) => (
<Product key={product.id} product={product} />
))}
</Grid>
</Grid>
</div>
);
};
export default ProductGrid;
Here is the child component Product:
const Product: React.FC<ProductTypeAsProps> = ({product}: ProductTypeAsProps) => {
const [loading, setLoading] = useState(true);
setTimeout(() => {
setLoading(false);
}, 3000);
return (
<Box className="product">
<Card className="card">
<CardContent className="content">
{loading ? (
<div className="skeleton">
<Skeleton
variant="text"
animation="wave"
component="h2"
width="65%"
/>
</div>
) : (
<div>
<p>{product.name}</p>
<p>{product.description}</p>
<p>{product.price.formatted_with_symbol}</p>
</div>
)}
</CardContent>
</Card>
</Box>
);
};
export default Product;
And the types declaration:
export type ProductType = {
id: string;
name: string;
price: {
formatted_with_symbol: string;
};
description: string;
assets: [
{
id: string;
filename: string;
url: string;
}
];
};
export interface ProductTypeAsProps {
product: ProductType;
}
I also have an .eslintrc :
{
"extends": ["airbnb-typescript", "react-app", "prettier"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"project": "tsconfig.json"
},
"plugins": ["prettier", "@typescript-eslint", "react-hooks"],
"settings": {
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
},
"rules": {
"@typescript-eslint/no-explicit-any": [
"error",
{
"ignoreRestArgs": true
}
],
}
}
and the tsconfig :
{
"compilerOptions": {
"target": "es5",
"lib": ["es6", "dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"noImplicitAny": false
},
"include": ["src"]
}
I have created this codesandbox but of course, the lint error does not show up in there, I'm coding this in VsCode.
Any bit of help? Cause this is pretty standard in javascript.
I can tell what the problem is by looking at the error message:
Argument of type '(product: ProductType) => JSX.Element' is not assignable to parameter of type '(value: object, index: number, array: object[]) => Element'.
This says that the map function that you are using inside products.map()
is of type (product: ProductType) => JSX.Element
. It says that this is not a valid mapper for your products
array because products.map()
expects a map function of type (value: object, index: number, array: object[]) => Element
.
Why does it expect this?
This error means that the products
variable in your actual code has the type object[]
. You are trying to make up for that vague type by using (product: ProductType)
inside your map()
but this will not work. Typing the argument here means that your map function can only handle objects of type ProductType
but it needs to handle any object
since products
is an array of object
.
What you need to do instead is make it so that your products
variable has the type ProductType[]
. I think you are getting this data from an API in the actual code? So make sure that the API call returns ProductType[]
instead of object[]
.
As a quick fix you can assert the type of the array before you map it, but it's always better to address the problem at the source.
{(products as ProductType[]).map((product) => (
<Product key={product.id} product={product} />
))}