Typescript: Is it possible to setState for only one key in exported type?

Viewed 334

I decided to try out and learn Typescript in my React App and I got into first error I can't figure out or find the answer for it.

Is it possible to access only one specific key in exported type and change its value?

So I am exporting my type like this:

export type MyFirstTestType = {
    id: number;
    name: string;
    price: number;
    object1: {
        name: string;
        price: number;
},
    object2: {
        name: string;
        price: number;
},

And then I am getting the values from my external API

const getTestItems = async (): Promise<MyFirstTestType[]> =>
    await (await fetch('apiEndpoint')).json();

In classic JS I would just create new state for price

 const [itemPrice, setItemPrice] = React.useState(data.price)

and then easily update state with onClick function. Is is possible to achieve something like this with TypeScript? So I can update only the price from MyFirstTestType?

I tried similar thing:

const [price, setPrice] = React.useState<MyFirstTestType>({price: 100}); 

But this throw error of -> Type '{ price: number; }' is missing the rest of the properties in MyFirstTestType (id, name,...)

2 Answers

The example in your question is misleading.

JS: const [itemPrice, setItemPrice] = React.useState(data.price)

TS: const [price, setPrice] = React.useState<MyFirstTestType>({price: 100});

In the JS example you're initializing itemPrice with a value from data.price (it's not clear where data come from). Note that while setItemPrice will update your itemPrice variable, it will not update the value on the data object as you seem to think. But in the TS example you're doing something different: you're intializing price with an entirely new object.

Now the point is: what kind of value is price going to hold?

If that will be a simple price (like in the JS example) you're type declaration is wrong, because you're declaring that value as MyFirstTestType.

If it will indeed hold a value of type MyFirstTestType then you must certainly pass a valid MyFirstTestType instance as initial value (or undefined). And how a valid MyFirstTestType instance looks like, depends on your type declaration.

But note that either in JS or in TS you should treat your state as immutable: the only difference here is type checking.

First off you should use interface instead of type

You can use another type for your setState type that extends the origin type

export interface MyFirstTestType {
  id: number;
  name: string;
  price: number;
  object1: {
    name: string;
    price: number;
  };
  object2: {
    name: string;
    price: number;
  };
}
export interface MyFirstTestTypeState extends Partial<MyFirstTestType> {
  name?: string;
  price: number;
  object1?: {
    name: string;
    price: number;
  };
  object2?: {
    name: string;
    price: number;
  };
}

In this case id and other would be optional only for state but for your request you can use the one that id and other are not optional, you can still see MyFirstTestType properties and use them the only difference is that you now can update any property you want

Now import it and then in your use state

  const [price, setPrice] = useState<MyFirstTestTypeState>({price:2});

Here is the sandbox example
https://codesandbox.io/s/cranky-hooks-zwisw?file=/src/App.tsx:154-224

Related