简体   繁体   中英

How to use nested objects in TypeScript? Property does not exist on type 'object'

I have a React/GraphQL small application working fine, and I'm trying to add TypeScript to it, as I'm new to TypeScript and trying to learn it. I have a GraphQL API endpoint that is returning products and information on those products. Using React, I'm storing products in a state and then rendering each product using JSX.

I created a type Product with the information that I expect the API to return, but it's also returning nested objects, and everything I tried triggers a TypeScript error. This is a simplified example of what the API is returning:

{
    "data": {
        "products": [
            {
                "productName": "Soda",
                "code": "sod",
                "prices": {
                    "nationalPrices": {
                        "localPrices": [
                            {
                                "pricePerUnit": "1.99"
                            }
                        ],
                        "regionalPrices": [
                            {
                                "pricePerUnit": "2.49"
                            }
                        ]
                    },
                    "vendorPrices": {
                        "localPrices": [
                            {
                                "pricePerUnit": "1.49"
                            }
                        ]
                    }
                }
            },
            // more products... 
        ]
    }
}

And this is the solution I currently have. code , productName and prices are working fine, but nationalPrices is triggering a TypeScript error property nationalPrices does not exist on type 'object' . What can I do to fix this error?

type nationalPricesObject = {
    localPrices: object[];
}

type Product = {
    code: string;
    productName: string;
    prices: object;
    nationalPrices: nationalPricesObject;
}


function ProductList() {
    const [products, setProducts] = useState < Product[] > ([]);
    const { loading, data, error } = useQuery(LOAD_PRODUCTS);

    useEffect(() => {
        if (data) {
            setProducts(data.products);
        }
    }, [data]);

    return (
        <>
            <div>
                {products.map(p =>
                (
                    <div>
                        <ProductCard
                            productName={p.displayName}
                            code={p.code}
                            // simplified code below for sake of clearity 
                            pricePerUnit={p.prices.nationalPrices.localPrices[0]['pricePerUnit']}
                        />
                    </div>
                ))}
            </div>
        </>
    )
}

Product doesn't contain a nationalPrices property. Product.prices does. We can set a type Prices which contains the stuff expected in Product.prices, and use that to define the prices property of the Product object.

type Prices = {
    nationalPrices: nationalPricesObject;
    vendorPrices: someOtherPricesObject;
}

type Product = {
    code: string;
    productName: string;
    prices: Prices;
}

Usage of object is tricky in typescript, and generally not recommended. You have two problems here:

  1. object is an ambiguous type, so you cannot use prices.nationalPrices because nationalPrices does not exist on type object
  2. Once you fix this, you will also encounter an error that localPrices[0] which is of type object does not have pricePerUnit

To fix these, make you types more specific:

type nationalPricesObject = {
    localPrices: { pricePerUnit: number; }[]; // assuming this is number?
}

type Product = {
    code: string;
    productName: string;
    prices: nationalPricesObject;
}

Lastly, by convention, types in typescript should be PascalCase , and interfaces should be preferred. So, I would change your code to look like:

interface Price {
  pricePerUnit: number;
}

interface NationalPrices {
    localPrices: Price[];
}

interface Product {
    code: string;
    productName: string;
    prices: NationalPrices;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM