If I understand your model right, the Item is just a count, and it's the Basket's 'name' field which actually says what the item is? Assuming I have that right, check this out:
-- A basket is the association of an item with a count.
data Basket = Basket {item :: String, count :: Int}
-- Open and closed baskets are just baskets with different behavior.
newtype OpenBasket = OpenBasket {getOpenBasket :: Basket}
newtype ClosedBasket = ClosedBasket {getClosedBasket :: Basket}
-- A cart can contain a mix of both basket types.
type CartBasket = Either OpenBasket ClosedBasket
data Cart = List CartBasket
-- We'll want to conveniently inspect CartBaskets.
getCartBasket :: CartBasket -> Basket
getCartBasket = either getOpenBasket getClosedBasket
-- This function accepts only OpenBaskets. No need for a runtime check.
addToOpenBasket :: OpenBasket -> OpenBasket
addToOpenBasket (OpenBasket b@(Basket _ i))
= OpenBasket (b {count = i+1})
-- First-class functions let you keep this safety all the way to the top...
data UserControl a = UserControl {label :: String, behavior :: a -> a}
-- Only allow the user to see valid behaviors!
getAddToCartBasketUserControl :: CartBasket -> UserControl CartBasket
getAddToCartBasketUserControl =
let f = either (Left . addToOpenBasket) Right
in either (const (UserControl "Add" f)) (const (UserControl "Locked" f))