Code for an interface, not for an implementation. That is to say, variables should not be declared with the type of the subtypes but with the type of the supertypes.
But that easily runs into problems of type slicing when I actually have to obtain the  type of the subclass to perform specific operations.
How is the problem of type erasure normally solved in reality?
(Understood "type erasure" in this context, but actually it is used in Java for generic type parameters as used in collections.)
Apparently the subject is having a collection, say a list of an interface.
The principle interface over class has several well founded reasons. For the reader still not familiar with those, read about them, they improve expressive coding enormously.
1 All items the same class
The case where all list items are actually of the same class means that the interface is not complete. Again in java you can have an interface extending more than one interface. Or you can look for a more specific interface. If you have an interface Set implemented as class TreeSet but expect that the items are ordered like in TreeSet pick the interface SortedSet. The operational requirements must be satisfied by the typing. This might mean using a class instead. For a class bound to a local application there may very well be no reason to artificially split it in interface and implementing class = a wrong legalistic approach, costing time.
2 Items with very different classes
The case where all list items are actually of the different case classes which later need to be handled per case class, is painful.
One may assume that the use of the specific class may not be captured in a more extended interface. That would be easy. So actually you threw together things that still needed to be kept separate. Keep the items in separate lists (too). As already mentioned for the case of lists of same class implementations they may expose the class not the interface. Keep the cutlery separate.
3 Different classes, but you must live with them
The last case concerns having to live with mixture of things, needed to be treated differently. An example (in java):
List<Animal> animals = ...
List<Swimming> swimmers = animals
    .stream()
    .filter(a -> a instanceof Swimming)
    .map(Swimming.class::cast)
    .collect(Collectors.toList())
;
List<Flying> flyers = animals
    .stream()
    .filter(a -> a instanceof Flying)
    .map(Flying.class::cast)
    .collect(Collectors.toList())
;
This case not necessarily is based on inheritance (as here with instanceof). You might have:
interface/class Animal {
    Optional<Swimming> asSwimming();
    <T> Optional<T> as(Class<T> type);
    ...
Following the single responsibility principle: then keep code for every implementation class in a separate source. There is not much more you can do about that. It  happens.