skip to Main Content

I have the following TypeScript error when using useState Argument of type '(previousState: IsProducts) => { list: IsProduct[]; }' is not assignable to parameter of type 'IsProducts'.

I’m not too sure what the issue is with the type. If anyone can point me in the right direction it would be appreciated 🙂


export interface IsProduct {
    name: string;
    price: number;
    quantity: number;
}

export interface IsProducts {
    list: IsProduct[];
}

const [products, setProducts] = useState<IsProducts | undefined>({
        list: [
            {
                name: 'Test One',
                price: 20,
                quantity: 2,
            },
            {
                name: 'Test Two',
                price: 10,
                quantity: 1,
            },
        ],
    });

setProducts((previousState: IsProducts) => {
                return {
                    ...previousState,
                    list: previousState.list.map((product: IsProduct, idx: number) => {
                        if (idx === index) {
                            return {
                                ...product,
                                quantity: product.quantity >= 10 ? (product.quantity = 10) : product.quantity + 1,
                            };
                        }
                        return product;
                    }),
                };
            });

Removing setProducts from IsProductContext removes the above error, but throws a different error on context.

export interface IsProductContext {
    products: IsProducts;
    setProducts: (products: IsProducts) => void;
}

2

Answers


  1. When you invoke useState you parametrized it with IsProducts | undefined, and then when you call setProducts you have previousState: IsProducts. You have mismatch types.

    Login or Signup to reply.
  2. Expanding on a comment. You create setProducts with useState:

    const [products, setProducts] = useState<IsProducts | undefined>(...);
    

    At this point, setProducts has this somewhat opaque and unhelpful type:

    React.Dispatch<React.SetStateAction<IsProducts | undefined>>
    

    Inspecting the React typings you can confirm that these are aliases for more familiar constructs:

    type Dispatch<A> = (value: A) => void;
    
    type SetStateAction<S> = ((prevState: S) => S) | S;
    

    No surprises – we can call setProducts(..) either with an IsProducts | undefined value, or a function that returns an IsProducts | undefined value.

    Your context type is:

    export interface IsProductContext {
        products: IsProducts;
        setProducts: (products: IsProducts) => void;
    }
    

    setProducts in the context has a different type from setProducts as returned by useState – context.setProducts only advertises that it accepts an IsProducts value, not the callback form. When you supply it with a callback value, Typescript rightly reports an error.

    If you update the context type to match React’s typings for the setter returned by useState

    export interface IsProductContext {
        products: IsProducts;
        setProducts: React.Dispatch<React.SetStateAction<IsProducts | undefined>>;
    }
    

    … then you’re all good.

    As a side-note, in my projects I generally set up a couple of aliases for these type of state update functions, to decouple them from React more than anything else:

    type StateSetter<T> = (update: StateUpdate<T>) => void;
    type StateUpdate<T> = T | ((prevT: T) => T);
    

    in which case you would have setProducts: StateSetter<IsProducts | undefined> in your context.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search