You should use composition rather than inheritance. IMHO for games in particular inheritance deeper than one level (where it can come useful in some cases) is questionable.
Think about it - what if a potion could do multiple things? Inheritance becomes a tangled mess of relationships, whereas you could instead have a Potion class, which takes a list of Attributes which give a certain effect to the Player drinking them (say, +20 health, +50 strength for some time, cure all poisons, ..) and those Attributes could themselves be composed of smaller attributes (e.g. "cure" and "cure target: burn") in order to allow very complex effects while keeping the logic small and self-contained.
If you want to go full entity-component-system, then you don't even need the Potion class. You can just have an entity with components "name" (Potion of ...), "drinkable" (for instance), and then a bunch of Attributes that will be applied when drinking. But that may be overkill, or might not map well to the particular gameplay you have in mind.
Think about the first example, and see if you understand it, see if you can draw the benefits of using that approach. It will likely make your code much easier to reason about than using brute force inheritance.
I actually kind of like the full entity-component-system idea, although it would probably get really complicated at some point like you said. However, it does bring up one issue that I wish I had thought about before hand (that doesn't necessarily deal with the full ecs): When the player does something that would result in finding an item in the world (opening a chest, killing something, resource gathering as some basic examples) and the item object is created, how exactly is the information for the specific item obtained?
For example, when my friend made a small text-based game in Java he made a static class that contained an array of item objects and just copied the item needed into the instance found. Probably a horrible idea for a bigger project like mine but it kinda shows what I'm getting at.
For my project, I would do something like:
Item item1 = new Item(components...);
where the components are things like name, sell value, isSellable, etc. In a lot of cases, I want the items that are found in many cases to be random. So when the random item is determined where should the information be pulled from? A txt file?